xref: /openbmc/linux/sound/core/info.c (revision aee0c612)
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 /*
371da177e4SLinus Torvalds  *
381da177e4SLinus Torvalds  */
391da177e4SLinus Torvalds 
40e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS
41e28563ccSTakashi Iwai 
421da177e4SLinus Torvalds int snd_info_check_reserved_words(const char *str)
431da177e4SLinus Torvalds {
441da177e4SLinus Torvalds 	static char *reserved[] =
451da177e4SLinus Torvalds 	{
461da177e4SLinus Torvalds 		"version",
471da177e4SLinus Torvalds 		"meminfo",
481da177e4SLinus Torvalds 		"memdebug",
491da177e4SLinus Torvalds 		"detect",
501da177e4SLinus Torvalds 		"devices",
511da177e4SLinus Torvalds 		"oss",
521da177e4SLinus Torvalds 		"cards",
531da177e4SLinus Torvalds 		"timers",
541da177e4SLinus Torvalds 		"synth",
551da177e4SLinus Torvalds 		"pcm",
561da177e4SLinus Torvalds 		"seq",
571da177e4SLinus Torvalds 		NULL
581da177e4SLinus Torvalds 	};
591da177e4SLinus Torvalds 	char **xstr = reserved;
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds 	while (*xstr) {
621da177e4SLinus Torvalds 		if (!strcmp(*xstr, str))
631da177e4SLinus Torvalds 			return 0;
641da177e4SLinus Torvalds 		xstr++;
651da177e4SLinus Torvalds 	}
661da177e4SLinus Torvalds 	if (!strncmp(str, "card", 4))
671da177e4SLinus Torvalds 		return 0;
681da177e4SLinus Torvalds 	return 1;
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
711a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex);
721da177e4SLinus Torvalds 
7324c1f931STakashi Iwai struct snd_info_private_data {
7424c1f931STakashi Iwai 	struct snd_info_buffer *rbuffer;
7524c1f931STakashi Iwai 	struct snd_info_buffer *wbuffer;
7624c1f931STakashi Iwai 	struct snd_info_entry *entry;
771da177e4SLinus Torvalds 	void *file_private_data;
7824c1f931STakashi Iwai };
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds static int snd_info_version_init(void);
811da177e4SLinus Torvalds static int snd_info_version_done(void);
82746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry);
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds 
857e4eeec8STakashi Iwai /* resize the proc r/w buffer */
867e4eeec8STakashi Iwai static int resize_info_buffer(struct snd_info_buffer *buffer,
877e4eeec8STakashi Iwai 			      unsigned int nsize)
887e4eeec8STakashi Iwai {
897e4eeec8STakashi Iwai 	char *nbuf;
907e4eeec8STakashi Iwai 
917e4eeec8STakashi Iwai 	nsize = PAGE_ALIGN(nsize);
929983aa62STakashi Iwai 	nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
937e4eeec8STakashi Iwai 	if (! nbuf)
947e4eeec8STakashi Iwai 		return -ENOMEM;
957e4eeec8STakashi Iwai 
967e4eeec8STakashi Iwai 	buffer->buffer = nbuf;
977e4eeec8STakashi Iwai 	buffer->len = nsize;
987e4eeec8STakashi Iwai 	return 0;
997e4eeec8STakashi Iwai }
1007e4eeec8STakashi Iwai 
1011da177e4SLinus Torvalds /**
1021da177e4SLinus Torvalds  * snd_iprintf - printf on the procfs buffer
1031da177e4SLinus Torvalds  * @buffer: the procfs buffer
1041da177e4SLinus Torvalds  * @fmt: the printf format
1051da177e4SLinus Torvalds  *
1061da177e4SLinus Torvalds  * Outputs the string on the procfs buffer just like printf().
1071da177e4SLinus Torvalds  *
1081da177e4SLinus Torvalds  * Returns the size of output string.
1091da177e4SLinus Torvalds  */
1104f7454a9STakashi Iwai int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
1111da177e4SLinus Torvalds {
1121da177e4SLinus Torvalds 	va_list args;
1131da177e4SLinus Torvalds 	int len, res;
1147e4eeec8STakashi Iwai 	int err = 0;
1151da177e4SLinus Torvalds 
116f001c3acSTakashi Iwai 	might_sleep();
1171da177e4SLinus Torvalds 	if (buffer->stop || buffer->error)
1181da177e4SLinus Torvalds 		return 0;
1191da177e4SLinus Torvalds 	len = buffer->len - buffer->size;
1201da177e4SLinus Torvalds 	va_start(args, fmt);
1217e4eeec8STakashi Iwai 	for (;;) {
122dbedca39STakashi Iwai 		va_list ap;
123dbedca39STakashi Iwai 		va_copy(ap, args);
124dbedca39STakashi Iwai 		res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap);
125dbedca39STakashi Iwai 		va_end(ap);
1267e4eeec8STakashi Iwai 		if (res < len)
1277e4eeec8STakashi Iwai 			break;
1287e4eeec8STakashi Iwai 		err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
1297e4eeec8STakashi Iwai 		if (err < 0)
1307e4eeec8STakashi Iwai 			break;
1317e4eeec8STakashi Iwai 		len = buffer->len - buffer->size;
1321da177e4SLinus Torvalds 	}
1337e4eeec8STakashi Iwai 	va_end(args);
1347e4eeec8STakashi Iwai 
1357e4eeec8STakashi Iwai 	if (err < 0)
1367e4eeec8STakashi Iwai 		return err;
1371da177e4SLinus Torvalds 	buffer->curr += res;
1381da177e4SLinus Torvalds 	buffer->size += res;
1391da177e4SLinus Torvalds 	return res;
1401da177e4SLinus Torvalds }
1411da177e4SLinus Torvalds 
142c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_iprintf);
143c0d3fb39STakashi Iwai 
1441da177e4SLinus Torvalds /*
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds  */
1471da177e4SLinus Torvalds 
1486581f4e7STakashi Iwai static struct proc_dir_entry *snd_proc_root;
1496581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root;
150c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root);
151c0d3fb39STakashi Iwai 
1521da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
1536581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root;
1541da177e4SLinus Torvalds #endif
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds static void snd_remove_proc_entry(struct proc_dir_entry *parent,
1571da177e4SLinus Torvalds 				  struct proc_dir_entry *de)
1581da177e4SLinus Torvalds {
1591da177e4SLinus Torvalds 	if (de)
1601da177e4SLinus Torvalds 		remove_proc_entry(de->name, parent);
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
1641da177e4SLinus Torvalds {
16524c1f931STakashi Iwai 	struct snd_info_private_data *data;
1661da177e4SLinus Torvalds 	struct snd_info_entry *entry;
16773029e0fSTakashi Iwai 	loff_t ret = -EINVAL, size;
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 	data = file->private_data;
1701da177e4SLinus Torvalds 	entry = data->entry;
1715b5cd553STakashi Iwai 	mutex_lock(&entry->access);
17273029e0fSTakashi Iwai 	if (entry->content == SNDRV_INFO_CONTENT_DATA &&
17373029e0fSTakashi Iwai 	    entry->c.ops->llseek) {
17473029e0fSTakashi Iwai 		offset = entry->c.ops->llseek(entry,
1751da177e4SLinus Torvalds 					      data->file_private_data,
1761da177e4SLinus Torvalds 					      file, offset, orig);
1771da177e4SLinus Torvalds 		goto out;
1781da177e4SLinus Torvalds 	}
17973029e0fSTakashi Iwai 	if (entry->content == SNDRV_INFO_CONTENT_DATA)
18073029e0fSTakashi Iwai 		size = entry->size;
18173029e0fSTakashi Iwai 	else
18273029e0fSTakashi Iwai 		size = 0;
18373029e0fSTakashi Iwai 	switch (orig) {
18473029e0fSTakashi Iwai 	case SEEK_SET:
1851da177e4SLinus Torvalds 		break;
18673029e0fSTakashi Iwai 	case SEEK_CUR:
18773029e0fSTakashi Iwai 		offset += file->f_pos;
18873029e0fSTakashi Iwai 		break;
18973029e0fSTakashi Iwai 	case SEEK_END:
19073029e0fSTakashi Iwai 		if (!size)
19173029e0fSTakashi Iwai 			goto out;
19273029e0fSTakashi Iwai 		offset += size;
19373029e0fSTakashi Iwai 		break;
19473029e0fSTakashi Iwai 	default:
19573029e0fSTakashi Iwai 		goto out;
1961da177e4SLinus Torvalds 	}
19773029e0fSTakashi Iwai 	if (offset < 0)
19873029e0fSTakashi Iwai 		goto out;
19973029e0fSTakashi Iwai 	if (size && offset > size)
20073029e0fSTakashi Iwai 		offset = size;
20173029e0fSTakashi Iwai 	file->f_pos = offset;
20273029e0fSTakashi Iwai 	ret = offset;
2031da177e4SLinus Torvalds  out:
2045b5cd553STakashi Iwai 	mutex_unlock(&entry->access);
2051da177e4SLinus Torvalds 	return ret;
2061da177e4SLinus Torvalds }
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
2091da177e4SLinus Torvalds 				   size_t count, loff_t * offset)
2101da177e4SLinus Torvalds {
21124c1f931STakashi Iwai 	struct snd_info_private_data *data;
2121da177e4SLinus Torvalds 	struct snd_info_entry *entry;
21324c1f931STakashi Iwai 	struct snd_info_buffer *buf;
2141da177e4SLinus Torvalds 	size_t size = 0;
2151da177e4SLinus Torvalds 	loff_t pos;
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 	data = file->private_data;
2187eaa943cSTakashi Iwai 	if (snd_BUG_ON(!data))
2197eaa943cSTakashi Iwai 		return -ENXIO;
2201da177e4SLinus Torvalds 	pos = *offset;
2211da177e4SLinus Torvalds 	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
2221da177e4SLinus Torvalds 		return -EIO;
2231da177e4SLinus Torvalds 	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
2241da177e4SLinus Torvalds 		return -EIO;
2251da177e4SLinus Torvalds 	entry = data->entry;
2261da177e4SLinus Torvalds 	switch (entry->content) {
2271da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_TEXT:
2281da177e4SLinus Torvalds 		buf = data->rbuffer;
2291da177e4SLinus Torvalds 		if (buf == NULL)
2301da177e4SLinus Torvalds 			return -EIO;
2311da177e4SLinus Torvalds 		if (pos >= buf->size)
2321da177e4SLinus Torvalds 			return 0;
2331da177e4SLinus Torvalds 		size = buf->size - pos;
2341da177e4SLinus Torvalds 		size = min(count, size);
2351da177e4SLinus Torvalds 		if (copy_to_user(buffer, buf->buffer + pos, size))
2361da177e4SLinus Torvalds 			return -EFAULT;
2371da177e4SLinus Torvalds 		break;
2381da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
239d97e1b78STakashi Iwai 		if (pos >= entry->size)
240d97e1b78STakashi Iwai 			return 0;
241d97e1b78STakashi Iwai 		if (entry->c.ops->read) {
242d97e1b78STakashi Iwai 			size = entry->size - pos;
243d97e1b78STakashi Iwai 			size = min(count, size);
2441da177e4SLinus Torvalds 			size = entry->c.ops->read(entry,
2451da177e4SLinus Torvalds 						  data->file_private_data,
246d97e1b78STakashi Iwai 						  file, buffer, size, pos);
247d97e1b78STakashi Iwai 		}
2481da177e4SLinus Torvalds 		break;
2491da177e4SLinus Torvalds 	}
2501da177e4SLinus Torvalds 	if ((ssize_t) size > 0)
2511da177e4SLinus Torvalds 		*offset = pos + size;
2521da177e4SLinus Torvalds 	return size;
2531da177e4SLinus Torvalds }
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
2561da177e4SLinus Torvalds 				    size_t count, loff_t * offset)
2571da177e4SLinus Torvalds {
25824c1f931STakashi Iwai 	struct snd_info_private_data *data;
2591da177e4SLinus Torvalds 	struct snd_info_entry *entry;
26024c1f931STakashi Iwai 	struct snd_info_buffer *buf;
2617e4eeec8STakashi Iwai 	ssize_t size = 0;
2621da177e4SLinus Torvalds 	loff_t pos;
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 	data = file->private_data;
2657eaa943cSTakashi Iwai 	if (snd_BUG_ON(!data))
2667eaa943cSTakashi Iwai 		return -ENXIO;
2671da177e4SLinus Torvalds 	entry = data->entry;
2681da177e4SLinus Torvalds 	pos = *offset;
2691da177e4SLinus Torvalds 	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
2701da177e4SLinus Torvalds 		return -EIO;
2711da177e4SLinus Torvalds 	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
2721da177e4SLinus Torvalds 		return -EIO;
2731da177e4SLinus Torvalds 	switch (entry->content) {
2741da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_TEXT:
2751da177e4SLinus Torvalds 		buf = data->wbuffer;
2761da177e4SLinus Torvalds 		if (buf == NULL)
2771da177e4SLinus Torvalds 			return -EIO;
278f4a747f1SClemens Ladisch 		mutex_lock(&entry->access);
2797e4eeec8STakashi Iwai 		if (pos + count >= buf->len) {
2807e4eeec8STakashi Iwai 			if (resize_info_buffer(buf, pos + count)) {
2817e4eeec8STakashi Iwai 				mutex_unlock(&entry->access);
2821da177e4SLinus Torvalds 				return -ENOMEM;
2837e4eeec8STakashi Iwai 			}
2847e4eeec8STakashi Iwai 		}
2857e4eeec8STakashi Iwai 		if (copy_from_user(buf->buffer + pos, buffer, count)) {
2867e4eeec8STakashi Iwai 			mutex_unlock(&entry->access);
2871da177e4SLinus Torvalds 			return -EFAULT;
2887e4eeec8STakashi Iwai 		}
2897e4eeec8STakashi Iwai 		buf->size = pos + count;
2907e4eeec8STakashi Iwai 		mutex_unlock(&entry->access);
2917e4eeec8STakashi Iwai 		size = count;
2921da177e4SLinus Torvalds 		break;
2931da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
294d97e1b78STakashi Iwai 		if (entry->c.ops->write && count > 0) {
295d97e1b78STakashi Iwai 			size_t maxsize = entry->size - pos;
296d97e1b78STakashi Iwai 			count = min(count, maxsize);
2971da177e4SLinus Torvalds 			size = entry->c.ops->write(entry,
2981da177e4SLinus Torvalds 						   data->file_private_data,
2991da177e4SLinus Torvalds 						   file, buffer, count, pos);
300d97e1b78STakashi Iwai 		}
3011da177e4SLinus Torvalds 		break;
3021da177e4SLinus Torvalds 	}
3031da177e4SLinus Torvalds 	if ((ssize_t) size > 0)
3041da177e4SLinus Torvalds 		*offset = pos + size;
3051da177e4SLinus Torvalds 	return size;
3061da177e4SLinus Torvalds }
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds static int snd_info_entry_open(struct inode *inode, struct file *file)
3091da177e4SLinus Torvalds {
31024c1f931STakashi Iwai 	struct snd_info_entry *entry;
31124c1f931STakashi Iwai 	struct snd_info_private_data *data;
31224c1f931STakashi Iwai 	struct snd_info_buffer *buffer;
3131da177e4SLinus Torvalds 	struct proc_dir_entry *p;
3141da177e4SLinus Torvalds 	int mode, err;
3151da177e4SLinus Torvalds 
3161a60d4c5SIngo Molnar 	mutex_lock(&info_mutex);
3171da177e4SLinus Torvalds 	p = PDE(inode);
31824c1f931STakashi Iwai 	entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
319746d4a02STakashi Iwai 	if (entry == NULL || ! entry->p) {
3201a60d4c5SIngo Molnar 		mutex_unlock(&info_mutex);
3211da177e4SLinus Torvalds 		return -ENODEV;
3221da177e4SLinus Torvalds 	}
3231da177e4SLinus Torvalds 	if (!try_module_get(entry->module)) {
3241da177e4SLinus Torvalds 		err = -EFAULT;
3251da177e4SLinus Torvalds 		goto __error1;
3261da177e4SLinus Torvalds 	}
3271da177e4SLinus Torvalds 	mode = file->f_flags & O_ACCMODE;
3281da177e4SLinus Torvalds 	if (mode == O_RDONLY || mode == O_RDWR) {
3297e4eeec8STakashi Iwai 		if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
3301da177e4SLinus Torvalds 		     entry->c.ops->read == NULL)) {
3311da177e4SLinus Torvalds 		    	err = -ENODEV;
3321da177e4SLinus Torvalds 		    	goto __error;
3331da177e4SLinus Torvalds 		}
3341da177e4SLinus Torvalds 	}
3351da177e4SLinus Torvalds 	if (mode == O_WRONLY || mode == O_RDWR) {
3367e4eeec8STakashi Iwai 		if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
3371da177e4SLinus Torvalds 		     entry->c.ops->write == NULL)) {
3381da177e4SLinus Torvalds 		    	err = -ENODEV;
3391da177e4SLinus Torvalds 		    	goto __error;
3401da177e4SLinus Torvalds 		}
3411da177e4SLinus Torvalds 	}
342ca2c0966STakashi Iwai 	data = kzalloc(sizeof(*data), GFP_KERNEL);
3431da177e4SLinus Torvalds 	if (data == NULL) {
3441da177e4SLinus Torvalds 		err = -ENOMEM;
3451da177e4SLinus Torvalds 		goto __error;
3461da177e4SLinus Torvalds 	}
3471da177e4SLinus Torvalds 	data->entry = entry;
3481da177e4SLinus Torvalds 	switch (entry->content) {
3491da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_TEXT:
3501da177e4SLinus Torvalds 		if (mode == O_RDONLY || mode == O_RDWR) {
351ca2c0966STakashi Iwai 			buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
3527e4eeec8STakashi Iwai 			if (buffer == NULL)
3537e4eeec8STakashi Iwai 				goto __nomem;
3541da177e4SLinus Torvalds 			data->rbuffer = buffer;
3557e4eeec8STakashi Iwai 			buffer->len = PAGE_SIZE;
3567e4eeec8STakashi Iwai 			buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
3577e4eeec8STakashi Iwai 			if (buffer->buffer == NULL)
3587e4eeec8STakashi Iwai 				goto __nomem;
3591da177e4SLinus Torvalds 		}
3601da177e4SLinus Torvalds 		if (mode == O_WRONLY || mode == O_RDWR) {
361ca2c0966STakashi Iwai 			buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
3627e4eeec8STakashi Iwai 			if (buffer == NULL)
3637e4eeec8STakashi Iwai 				goto __nomem;
3641da177e4SLinus Torvalds 			data->wbuffer = buffer;
3657e4eeec8STakashi Iwai 			buffer->len = PAGE_SIZE;
3667e4eeec8STakashi Iwai 			buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
3677e4eeec8STakashi Iwai 			if (buffer->buffer == NULL)
3687e4eeec8STakashi Iwai 				goto __nomem;
3691da177e4SLinus Torvalds 		}
3701da177e4SLinus Torvalds 		break;
3711da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:	/* data */
3721da177e4SLinus Torvalds 		if (entry->c.ops->open) {
3731da177e4SLinus Torvalds 			if ((err = entry->c.ops->open(entry, mode,
3741da177e4SLinus Torvalds 						      &data->file_private_data)) < 0) {
3751da177e4SLinus Torvalds 				kfree(data);
3761da177e4SLinus Torvalds 				goto __error;
3771da177e4SLinus Torvalds 			}
3781da177e4SLinus Torvalds 		}
3791da177e4SLinus Torvalds 		break;
3801da177e4SLinus Torvalds 	}
3811da177e4SLinus Torvalds 	file->private_data = data;
3821a60d4c5SIngo Molnar 	mutex_unlock(&info_mutex);
3831da177e4SLinus Torvalds 	if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
3841da177e4SLinus Torvalds 	    (mode == O_RDONLY || mode == O_RDWR)) {
3851da177e4SLinus Torvalds 		if (entry->c.text.read) {
3861a60d4c5SIngo Molnar 			mutex_lock(&entry->access);
3871da177e4SLinus Torvalds 			entry->c.text.read(entry, data->rbuffer);
3881a60d4c5SIngo Molnar 			mutex_unlock(&entry->access);
3891da177e4SLinus Torvalds 		}
3901da177e4SLinus Torvalds 	}
3911da177e4SLinus Torvalds 	return 0;
3921da177e4SLinus Torvalds 
3937e4eeec8STakashi Iwai  __nomem:
3947e4eeec8STakashi Iwai 	if (data->rbuffer) {
3957e4eeec8STakashi Iwai 		kfree(data->rbuffer->buffer);
3967e4eeec8STakashi Iwai 		kfree(data->rbuffer);
3977e4eeec8STakashi Iwai 	}
3987e4eeec8STakashi Iwai 	if (data->wbuffer) {
3997e4eeec8STakashi Iwai 		kfree(data->wbuffer->buffer);
4007e4eeec8STakashi Iwai 		kfree(data->wbuffer);
4017e4eeec8STakashi Iwai 	}
4027e4eeec8STakashi Iwai 	kfree(data);
4037e4eeec8STakashi Iwai 	err = -ENOMEM;
4041da177e4SLinus Torvalds       __error:
4051da177e4SLinus Torvalds 	module_put(entry->module);
4061da177e4SLinus Torvalds       __error1:
4071a60d4c5SIngo Molnar 	mutex_unlock(&info_mutex);
4081da177e4SLinus Torvalds 	return err;
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds static int snd_info_entry_release(struct inode *inode, struct file *file)
4121da177e4SLinus Torvalds {
41324c1f931STakashi Iwai 	struct snd_info_entry *entry;
41424c1f931STakashi Iwai 	struct snd_info_private_data *data;
4151da177e4SLinus Torvalds 	int mode;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	mode = file->f_flags & O_ACCMODE;
4181da177e4SLinus Torvalds 	data = file->private_data;
4191da177e4SLinus Torvalds 	entry = data->entry;
4201da177e4SLinus Torvalds 	switch (entry->content) {
4211da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_TEXT:
4227e4eeec8STakashi Iwai 		if (data->rbuffer) {
4237e4eeec8STakashi Iwai 			kfree(data->rbuffer->buffer);
4241da177e4SLinus Torvalds 			kfree(data->rbuffer);
4251da177e4SLinus Torvalds 		}
4267e4eeec8STakashi Iwai 		if (data->wbuffer) {
4271da177e4SLinus Torvalds 			if (entry->c.text.write) {
4281da177e4SLinus Torvalds 				entry->c.text.write(entry, data->wbuffer);
4291da177e4SLinus Torvalds 				if (data->wbuffer->error) {
4301da177e4SLinus Torvalds 					snd_printk(KERN_WARNING "data write error to %s (%i)\n",
4311da177e4SLinus Torvalds 						entry->name,
4321da177e4SLinus Torvalds 						data->wbuffer->error);
4331da177e4SLinus Torvalds 				}
4341da177e4SLinus Torvalds 			}
4357e4eeec8STakashi Iwai 			kfree(data->wbuffer->buffer);
4361da177e4SLinus Torvalds 			kfree(data->wbuffer);
4371da177e4SLinus Torvalds 		}
4381da177e4SLinus Torvalds 		break;
4391da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
4401da177e4SLinus Torvalds 		if (entry->c.ops->release)
4411da177e4SLinus Torvalds 			entry->c.ops->release(entry, mode,
4421da177e4SLinus Torvalds 					      data->file_private_data);
4431da177e4SLinus Torvalds 		break;
4441da177e4SLinus Torvalds 	}
4451da177e4SLinus Torvalds 	module_put(entry->module);
4461da177e4SLinus Torvalds 	kfree(data);
4471da177e4SLinus Torvalds 	return 0;
4481da177e4SLinus Torvalds }
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
4511da177e4SLinus Torvalds {
45224c1f931STakashi Iwai 	struct snd_info_private_data *data;
4531da177e4SLinus Torvalds 	struct snd_info_entry *entry;
4541da177e4SLinus Torvalds 	unsigned int mask;
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 	data = file->private_data;
4571da177e4SLinus Torvalds 	if (data == NULL)
4581da177e4SLinus Torvalds 		return 0;
4591da177e4SLinus Torvalds 	entry = data->entry;
4601da177e4SLinus Torvalds 	mask = 0;
4611da177e4SLinus Torvalds 	switch (entry->content) {
4621da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
4631da177e4SLinus Torvalds 		if (entry->c.ops->poll)
4641da177e4SLinus Torvalds 			return entry->c.ops->poll(entry,
4651da177e4SLinus Torvalds 						  data->file_private_data,
4661da177e4SLinus Torvalds 						  file, wait);
4671da177e4SLinus Torvalds 		if (entry->c.ops->read)
4681da177e4SLinus Torvalds 			mask |= POLLIN | POLLRDNORM;
4691da177e4SLinus Torvalds 		if (entry->c.ops->write)
4701da177e4SLinus Torvalds 			mask |= POLLOUT | POLLWRNORM;
4711da177e4SLinus Torvalds 		break;
4721da177e4SLinus Torvalds 	}
4731da177e4SLinus Torvalds 	return mask;
4741da177e4SLinus Torvalds }
4751da177e4SLinus Torvalds 
476d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
477d99e9889SIngo Molnar 				unsigned long arg)
4781da177e4SLinus Torvalds {
47924c1f931STakashi Iwai 	struct snd_info_private_data *data;
4801da177e4SLinus Torvalds 	struct snd_info_entry *entry;
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds 	data = file->private_data;
4831da177e4SLinus Torvalds 	if (data == NULL)
4841da177e4SLinus Torvalds 		return 0;
4851da177e4SLinus Torvalds 	entry = data->entry;
4861da177e4SLinus Torvalds 	switch (entry->content) {
4871da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
4881da177e4SLinus Torvalds 		if (entry->c.ops->ioctl)
4891da177e4SLinus Torvalds 			return entry->c.ops->ioctl(entry,
4901da177e4SLinus Torvalds 						   data->file_private_data,
4911da177e4SLinus Torvalds 						   file, cmd, arg);
4921da177e4SLinus Torvalds 		break;
4931da177e4SLinus Torvalds 	}
4941da177e4SLinus Torvalds 	return -ENOTTY;
4951da177e4SLinus Torvalds }
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
4981da177e4SLinus Torvalds {
499496ad9aaSAl Viro 	struct inode *inode = file_inode(file);
50024c1f931STakashi Iwai 	struct snd_info_private_data *data;
5011da177e4SLinus Torvalds 	struct snd_info_entry *entry;
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds 	data = file->private_data;
5041da177e4SLinus Torvalds 	if (data == NULL)
5051da177e4SLinus Torvalds 		return 0;
5061da177e4SLinus Torvalds 	entry = data->entry;
5071da177e4SLinus Torvalds 	switch (entry->content) {
5081da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
5091da177e4SLinus Torvalds 		if (entry->c.ops->mmap)
5101da177e4SLinus Torvalds 			return entry->c.ops->mmap(entry,
5111da177e4SLinus Torvalds 						  data->file_private_data,
5121da177e4SLinus Torvalds 						  inode, file, vma);
5131da177e4SLinus Torvalds 		break;
5141da177e4SLinus Torvalds 	}
5151da177e4SLinus Torvalds 	return -ENXIO;
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds 
5189c2e08c5SArjan van de Ven static const struct file_operations snd_info_entry_operations =
5191da177e4SLinus Torvalds {
5201da177e4SLinus Torvalds 	.owner =		THIS_MODULE,
5211da177e4SLinus Torvalds 	.llseek =		snd_info_entry_llseek,
5221da177e4SLinus Torvalds 	.read =			snd_info_entry_read,
5231da177e4SLinus Torvalds 	.write =		snd_info_entry_write,
5241da177e4SLinus Torvalds 	.poll =			snd_info_entry_poll,
525d99e9889SIngo Molnar 	.unlocked_ioctl =	snd_info_entry_ioctl,
5261da177e4SLinus Torvalds 	.mmap =			snd_info_entry_mmap,
5271da177e4SLinus Torvalds 	.open =			snd_info_entry_open,
5281da177e4SLinus Torvalds 	.release =		snd_info_entry_release,
5291da177e4SLinus Torvalds };
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds int __init snd_info_init(void)
5321da177e4SLinus Torvalds {
5331da177e4SLinus Torvalds 	struct proc_dir_entry *p;
5341da177e4SLinus Torvalds 
535e55d92b9SAl Viro 	p = proc_mkdir("asound", NULL);
5361da177e4SLinus Torvalds 	if (p == NULL)
5371da177e4SLinus Torvalds 		return -ENOMEM;
5381da177e4SLinus Torvalds 	snd_proc_root = p;
5391da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
5401da177e4SLinus Torvalds 	{
54124c1f931STakashi Iwai 		struct snd_info_entry *entry;
5421da177e4SLinus Torvalds 		if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
5431da177e4SLinus Torvalds 			return -ENOMEM;
5441da177e4SLinus Torvalds 		entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
5451da177e4SLinus Torvalds 		if (snd_info_register(entry) < 0) {
5461da177e4SLinus Torvalds 			snd_info_free_entry(entry);
5471da177e4SLinus Torvalds 			return -ENOMEM;
5481da177e4SLinus Torvalds 		}
5491da177e4SLinus Torvalds 		snd_oss_root = entry;
5501da177e4SLinus Torvalds 	}
5511da177e4SLinus Torvalds #endif
5521da177e4SLinus Torvalds #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
5531da177e4SLinus Torvalds 	{
55424c1f931STakashi Iwai 		struct snd_info_entry *entry;
5551da177e4SLinus Torvalds 		if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
5561da177e4SLinus Torvalds 			return -ENOMEM;
5571da177e4SLinus Torvalds 		entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
5581da177e4SLinus Torvalds 		if (snd_info_register(entry) < 0) {
5591da177e4SLinus Torvalds 			snd_info_free_entry(entry);
5601da177e4SLinus Torvalds 			return -ENOMEM;
5611da177e4SLinus Torvalds 		}
5621da177e4SLinus Torvalds 		snd_seq_root = entry;
5631da177e4SLinus Torvalds 	}
5641da177e4SLinus Torvalds #endif
5651da177e4SLinus Torvalds 	snd_info_version_init();
5661da177e4SLinus Torvalds 	snd_minor_info_init();
5671da177e4SLinus Torvalds 	snd_minor_info_oss_init();
5681da177e4SLinus Torvalds 	snd_card_info_init();
5691da177e4SLinus Torvalds 	return 0;
5701da177e4SLinus Torvalds }
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds int __exit snd_info_done(void)
5731da177e4SLinus Torvalds {
5741da177e4SLinus Torvalds 	snd_card_info_done();
5751da177e4SLinus Torvalds 	snd_minor_info_oss_done();
5761da177e4SLinus Torvalds 	snd_minor_info_done();
5771da177e4SLinus Torvalds 	snd_info_version_done();
5781da177e4SLinus Torvalds 	if (snd_proc_root) {
5791da177e4SLinus Torvalds #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
580746d4a02STakashi Iwai 		snd_info_free_entry(snd_seq_root);
5811da177e4SLinus Torvalds #endif
5821da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
583746d4a02STakashi Iwai 		snd_info_free_entry(snd_oss_root);
5841da177e4SLinus Torvalds #endif
585c74c120aSAlexey Dobriyan 		snd_remove_proc_entry(NULL, snd_proc_root);
5861da177e4SLinus Torvalds 	}
5871da177e4SLinus Torvalds 	return 0;
5881da177e4SLinus Torvalds }
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds /*
5911da177e4SLinus Torvalds 
5921da177e4SLinus Torvalds  */
5931da177e4SLinus Torvalds 
5941da177e4SLinus Torvalds 
5951da177e4SLinus Torvalds /*
5961da177e4SLinus Torvalds  * create a card proc file
5971da177e4SLinus Torvalds  * called from init.c
5981da177e4SLinus Torvalds  */
59924c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card)
6001da177e4SLinus Torvalds {
6011da177e4SLinus Torvalds 	char str[8];
60224c1f931STakashi Iwai 	struct snd_info_entry *entry;
6031da177e4SLinus Torvalds 
6047eaa943cSTakashi Iwai 	if (snd_BUG_ON(!card))
6057eaa943cSTakashi Iwai 		return -ENXIO;
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 	sprintf(str, "card%i", card->number);
6081da177e4SLinus Torvalds 	if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
6091da177e4SLinus Torvalds 		return -ENOMEM;
6101da177e4SLinus Torvalds 	entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
6111da177e4SLinus Torvalds 	if (snd_info_register(entry) < 0) {
6121da177e4SLinus Torvalds 		snd_info_free_entry(entry);
6131da177e4SLinus Torvalds 		return -ENOMEM;
6141da177e4SLinus Torvalds 	}
6151da177e4SLinus Torvalds 	card->proc_root = entry;
6161da177e4SLinus Torvalds 	return 0;
6171da177e4SLinus Torvalds }
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds /*
6201da177e4SLinus Torvalds  * register the card proc file
6211da177e4SLinus Torvalds  * called from init.c
6221da177e4SLinus Torvalds  */
62324c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card)
6241da177e4SLinus Torvalds {
6251da177e4SLinus Torvalds 	struct proc_dir_entry *p;
6261da177e4SLinus Torvalds 
6277eaa943cSTakashi Iwai 	if (snd_BUG_ON(!card))
6287eaa943cSTakashi Iwai 		return -ENXIO;
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds 	if (!strcmp(card->id, card->proc_root->name))
6311da177e4SLinus Torvalds 		return 0;
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds 	p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
6341da177e4SLinus Torvalds 	if (p == NULL)
6351da177e4SLinus Torvalds 		return -ENOMEM;
6361da177e4SLinus Torvalds 	card->proc_root_link = p;
6371da177e4SLinus Torvalds 	return 0;
6381da177e4SLinus Torvalds }
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds /*
641c2eb9c4eSJaroslav Kysela  * called on card->id change
642c2eb9c4eSJaroslav Kysela  */
643c2eb9c4eSJaroslav Kysela void snd_info_card_id_change(struct snd_card *card)
644c2eb9c4eSJaroslav Kysela {
645c2eb9c4eSJaroslav Kysela 	mutex_lock(&info_mutex);
646c2eb9c4eSJaroslav Kysela 	if (card->proc_root_link) {
647c2eb9c4eSJaroslav Kysela 		snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
648c2eb9c4eSJaroslav Kysela 		card->proc_root_link = NULL;
649c2eb9c4eSJaroslav Kysela 	}
650c2eb9c4eSJaroslav Kysela 	if (strcmp(card->id, card->proc_root->name))
651c2eb9c4eSJaroslav Kysela 		card->proc_root_link = proc_symlink(card->id,
652c2eb9c4eSJaroslav Kysela 						    snd_proc_root,
653c2eb9c4eSJaroslav Kysela 						    card->proc_root->name);
654c2eb9c4eSJaroslav Kysela 	mutex_unlock(&info_mutex);
655c2eb9c4eSJaroslav Kysela }
656c2eb9c4eSJaroslav Kysela 
657c2eb9c4eSJaroslav Kysela /*
6581da177e4SLinus Torvalds  * de-register the card proc file
6591da177e4SLinus Torvalds  * called from init.c
6601da177e4SLinus Torvalds  */
661746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card)
6621da177e4SLinus Torvalds {
6637eaa943cSTakashi Iwai 	if (!card)
6647eaa943cSTakashi Iwai 		return;
665746d4a02STakashi Iwai 	mutex_lock(&info_mutex);
6661da177e4SLinus Torvalds 	if (card->proc_root_link) {
6671da177e4SLinus Torvalds 		snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
6681da177e4SLinus Torvalds 		card->proc_root_link = NULL;
6691da177e4SLinus Torvalds 	}
670746d4a02STakashi Iwai 	if (card->proc_root)
671746d4a02STakashi Iwai 		snd_info_disconnect(card->proc_root);
672746d4a02STakashi Iwai 	mutex_unlock(&info_mutex);
6731da177e4SLinus Torvalds }
674746d4a02STakashi Iwai 
675746d4a02STakashi Iwai /*
676746d4a02STakashi Iwai  * release the card proc file resources
677746d4a02STakashi Iwai  * called from init.c
678746d4a02STakashi Iwai  */
679746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card)
680746d4a02STakashi Iwai {
6817eaa943cSTakashi Iwai 	if (!card)
6827eaa943cSTakashi Iwai 		return 0;
683746d4a02STakashi Iwai 	snd_info_free_entry(card->proc_root);
684746d4a02STakashi Iwai 	card->proc_root = NULL;
6851da177e4SLinus Torvalds 	return 0;
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds /**
6901da177e4SLinus Torvalds  * snd_info_get_line - read one line from the procfs buffer
6911da177e4SLinus Torvalds  * @buffer: the procfs buffer
6921da177e4SLinus Torvalds  * @line: the buffer to store
6931da177e4SLinus Torvalds  * @len: the max. buffer size - 1
6941da177e4SLinus Torvalds  *
6951da177e4SLinus Torvalds  * Reads one line from the buffer and stores the string.
6961da177e4SLinus Torvalds  *
6971da177e4SLinus Torvalds  * Returns zero if successful, or 1 if error or EOF.
6981da177e4SLinus Torvalds  */
69924c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
7001da177e4SLinus Torvalds {
7011da177e4SLinus Torvalds 	int c = -1;
7021da177e4SLinus Torvalds 
7031da177e4SLinus Torvalds 	if (len <= 0 || buffer->stop || buffer->error)
7041da177e4SLinus Torvalds 		return 1;
7051da177e4SLinus Torvalds 	while (--len > 0) {
7067e4eeec8STakashi Iwai 		c = buffer->buffer[buffer->curr++];
7071da177e4SLinus Torvalds 		if (c == '\n') {
7087e4eeec8STakashi Iwai 			if (buffer->curr >= buffer->size)
7091da177e4SLinus Torvalds 				buffer->stop = 1;
7101da177e4SLinus Torvalds 			break;
7111da177e4SLinus Torvalds 		}
7121da177e4SLinus Torvalds 		*line++ = c;
7137e4eeec8STakashi Iwai 		if (buffer->curr >= buffer->size) {
7141da177e4SLinus Torvalds 			buffer->stop = 1;
7151da177e4SLinus Torvalds 			break;
7161da177e4SLinus Torvalds 		}
7171da177e4SLinus Torvalds 	}
7181da177e4SLinus Torvalds 	while (c != '\n' && !buffer->stop) {
7197e4eeec8STakashi Iwai 		c = buffer->buffer[buffer->curr++];
7207e4eeec8STakashi Iwai 		if (buffer->curr >= buffer->size)
7211da177e4SLinus Torvalds 			buffer->stop = 1;
7221da177e4SLinus Torvalds 	}
7231da177e4SLinus Torvalds 	*line = '\0';
7241da177e4SLinus Torvalds 	return 0;
7251da177e4SLinus Torvalds }
7261da177e4SLinus Torvalds 
727c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line);
728c0d3fb39STakashi Iwai 
7291da177e4SLinus Torvalds /**
730856def8aSHenrik Kretzschmar  * snd_info_get_str - parse a string token
7311da177e4SLinus Torvalds  * @dest: the buffer to store the string token
7321da177e4SLinus Torvalds  * @src: the original string
7331da177e4SLinus Torvalds  * @len: the max. length of token - 1
7341da177e4SLinus Torvalds  *
7351da177e4SLinus Torvalds  * Parses the original string and copy a token to the given
7361da177e4SLinus Torvalds  * string buffer.
7371da177e4SLinus Torvalds  *
7381da177e4SLinus Torvalds  * Returns the updated pointer of the original string so that
7391da177e4SLinus Torvalds  * it can be used for the next call.
7401da177e4SLinus Torvalds  */
7414f7454a9STakashi Iwai const char *snd_info_get_str(char *dest, const char *src, int len)
7421da177e4SLinus Torvalds {
7431da177e4SLinus Torvalds 	int c;
7441da177e4SLinus Torvalds 
7451da177e4SLinus Torvalds 	while (*src == ' ' || *src == '\t')
7461da177e4SLinus Torvalds 		src++;
7471da177e4SLinus Torvalds 	if (*src == '"' || *src == '\'') {
7481da177e4SLinus Torvalds 		c = *src++;
7491da177e4SLinus Torvalds 		while (--len > 0 && *src && *src != c) {
7501da177e4SLinus Torvalds 			*dest++ = *src++;
7511da177e4SLinus Torvalds 		}
7521da177e4SLinus Torvalds 		if (*src == c)
7531da177e4SLinus Torvalds 			src++;
7541da177e4SLinus Torvalds 	} else {
7551da177e4SLinus Torvalds 		while (--len > 0 && *src && *src != ' ' && *src != '\t') {
7561da177e4SLinus Torvalds 			*dest++ = *src++;
7571da177e4SLinus Torvalds 		}
7581da177e4SLinus Torvalds 	}
7591da177e4SLinus Torvalds 	*dest = 0;
7601da177e4SLinus Torvalds 	while (*src == ' ' || *src == '\t')
7611da177e4SLinus Torvalds 		src++;
7621da177e4SLinus Torvalds 	return src;
7631da177e4SLinus Torvalds }
7641da177e4SLinus Torvalds 
765c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str);
766c0d3fb39STakashi Iwai 
7671da177e4SLinus Torvalds /**
7681da177e4SLinus Torvalds  * snd_info_create_entry - create an info entry
7691da177e4SLinus Torvalds  * @name: the proc file name
7701da177e4SLinus Torvalds  *
7711da177e4SLinus Torvalds  * Creates an info entry with the given file name and initializes as
7721da177e4SLinus Torvalds  * the default state.
7731da177e4SLinus Torvalds  *
7741da177e4SLinus Torvalds  * Usually called from other functions such as
7751da177e4SLinus Torvalds  * snd_info_create_card_entry().
7761da177e4SLinus Torvalds  *
7771da177e4SLinus Torvalds  * Returns the pointer of the new instance, or NULL on failure.
7781da177e4SLinus Torvalds  */
77924c1f931STakashi Iwai static struct snd_info_entry *snd_info_create_entry(const char *name)
7801da177e4SLinus Torvalds {
78124c1f931STakashi Iwai 	struct snd_info_entry *entry;
782ca2c0966STakashi Iwai 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
7831da177e4SLinus Torvalds 	if (entry == NULL)
7841da177e4SLinus Torvalds 		return NULL;
785543537bdSPaulo Marques 	entry->name = kstrdup(name, GFP_KERNEL);
7861da177e4SLinus Torvalds 	if (entry->name == NULL) {
7871da177e4SLinus Torvalds 		kfree(entry);
7881da177e4SLinus Torvalds 		return NULL;
7891da177e4SLinus Torvalds 	}
7901da177e4SLinus Torvalds 	entry->mode = S_IFREG | S_IRUGO;
7911da177e4SLinus Torvalds 	entry->content = SNDRV_INFO_CONTENT_TEXT;
7921a60d4c5SIngo Molnar 	mutex_init(&entry->access);
793746d4a02STakashi Iwai 	INIT_LIST_HEAD(&entry->children);
794746d4a02STakashi Iwai 	INIT_LIST_HEAD(&entry->list);
7951da177e4SLinus Torvalds 	return entry;
7961da177e4SLinus Torvalds }
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds /**
7991da177e4SLinus Torvalds  * snd_info_create_module_entry - create an info entry for the given module
8001da177e4SLinus Torvalds  * @module: the module pointer
8011da177e4SLinus Torvalds  * @name: the file name
8021da177e4SLinus Torvalds  * @parent: the parent directory
8031da177e4SLinus Torvalds  *
8041da177e4SLinus Torvalds  * Creates a new info entry and assigns it to the given module.
8051da177e4SLinus Torvalds  *
8061da177e4SLinus Torvalds  * Returns the pointer of the new instance, or NULL on failure.
8071da177e4SLinus Torvalds  */
80824c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module,
8091da177e4SLinus Torvalds 					       const char *name,
81024c1f931STakashi Iwai 					       struct snd_info_entry *parent)
8111da177e4SLinus Torvalds {
81224c1f931STakashi Iwai 	struct snd_info_entry *entry = snd_info_create_entry(name);
8131da177e4SLinus Torvalds 	if (entry) {
8141da177e4SLinus Torvalds 		entry->module = module;
8151da177e4SLinus Torvalds 		entry->parent = parent;
8161da177e4SLinus Torvalds 	}
8171da177e4SLinus Torvalds 	return entry;
8181da177e4SLinus Torvalds }
8191da177e4SLinus Torvalds 
820c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry);
821c0d3fb39STakashi Iwai 
8221da177e4SLinus Torvalds /**
8231da177e4SLinus Torvalds  * snd_info_create_card_entry - create an info entry for the given card
8241da177e4SLinus Torvalds  * @card: the card instance
8251da177e4SLinus Torvalds  * @name: the file name
8261da177e4SLinus Torvalds  * @parent: the parent directory
8271da177e4SLinus Torvalds  *
8281da177e4SLinus Torvalds  * Creates a new info entry and assigns it to the given card.
8291da177e4SLinus Torvalds  *
8301da177e4SLinus Torvalds  * Returns the pointer of the new instance, or NULL on failure.
8311da177e4SLinus Torvalds  */
83224c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
8331da177e4SLinus Torvalds 					     const char *name,
83424c1f931STakashi Iwai 					     struct snd_info_entry * parent)
8351da177e4SLinus Torvalds {
83624c1f931STakashi Iwai 	struct snd_info_entry *entry = snd_info_create_entry(name);
8371da177e4SLinus Torvalds 	if (entry) {
8381da177e4SLinus Torvalds 		entry->module = card->module;
8391da177e4SLinus Torvalds 		entry->card = card;
8401da177e4SLinus Torvalds 		entry->parent = parent;
8411da177e4SLinus Torvalds 	}
8421da177e4SLinus Torvalds 	return entry;
8431da177e4SLinus Torvalds }
8441da177e4SLinus Torvalds 
845c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry);
846c0d3fb39STakashi Iwai 
847746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry)
848746d4a02STakashi Iwai {
849746d4a02STakashi Iwai 	struct list_head *p, *n;
850746d4a02STakashi Iwai 	struct proc_dir_entry *root;
851746d4a02STakashi Iwai 
852746d4a02STakashi Iwai 	list_for_each_safe(p, n, &entry->children) {
853746d4a02STakashi Iwai 		snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
854746d4a02STakashi Iwai 	}
855746d4a02STakashi Iwai 
856746d4a02STakashi Iwai 	if (! entry->p)
857746d4a02STakashi Iwai 		return;
858746d4a02STakashi Iwai 	list_del_init(&entry->list);
859746d4a02STakashi Iwai 	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
8607eaa943cSTakashi Iwai 	snd_BUG_ON(!root);
861746d4a02STakashi Iwai 	snd_remove_proc_entry(root, entry->p);
862746d4a02STakashi Iwai 	entry->p = NULL;
863746d4a02STakashi Iwai }
864746d4a02STakashi Iwai 
86524c1f931STakashi Iwai static int snd_info_dev_free_entry(struct snd_device *device)
8661da177e4SLinus Torvalds {
86724c1f931STakashi Iwai 	struct snd_info_entry *entry = device->device_data;
8681da177e4SLinus Torvalds 	snd_info_free_entry(entry);
8691da177e4SLinus Torvalds 	return 0;
8701da177e4SLinus Torvalds }
8711da177e4SLinus Torvalds 
87224c1f931STakashi Iwai static int snd_info_dev_register_entry(struct snd_device *device)
8731da177e4SLinus Torvalds {
87424c1f931STakashi Iwai 	struct snd_info_entry *entry = device->device_data;
8751da177e4SLinus Torvalds 	return snd_info_register(entry);
8761da177e4SLinus Torvalds }
8771da177e4SLinus Torvalds 
8781da177e4SLinus Torvalds /**
8791da177e4SLinus Torvalds  * snd_card_proc_new - create an info entry for the given card
8801da177e4SLinus Torvalds  * @card: the card instance
8811da177e4SLinus Torvalds  * @name: the file name
8821da177e4SLinus Torvalds  * @entryp: the pointer to store the new info entry
8831da177e4SLinus Torvalds  *
8841da177e4SLinus Torvalds  * Creates a new info entry and assigns it to the given card.
8851da177e4SLinus Torvalds  * Unlike snd_info_create_card_entry(), this function registers the
8861da177e4SLinus Torvalds  * info entry as an ALSA device component, so that it can be
8871da177e4SLinus Torvalds  * unregistered/released without explicit call.
8881da177e4SLinus Torvalds  * Also, you don't have to register this entry via snd_info_register(),
8891da177e4SLinus Torvalds  * since this will be registered by snd_card_register() automatically.
8901da177e4SLinus Torvalds  *
8911da177e4SLinus Torvalds  * The parent is assumed as card->proc_root.
8921da177e4SLinus Torvalds  *
8931da177e4SLinus Torvalds  * For releasing this entry, use snd_device_free() instead of
8941da177e4SLinus Torvalds  * snd_info_free_entry().
8951da177e4SLinus Torvalds  *
8961da177e4SLinus Torvalds  * Returns zero if successful, or a negative error code on failure.
8971da177e4SLinus Torvalds  */
89824c1f931STakashi Iwai int snd_card_proc_new(struct snd_card *card, const char *name,
89924c1f931STakashi Iwai 		      struct snd_info_entry **entryp)
9001da177e4SLinus Torvalds {
90124c1f931STakashi Iwai 	static struct snd_device_ops ops = {
9021da177e4SLinus Torvalds 		.dev_free = snd_info_dev_free_entry,
9031da177e4SLinus Torvalds 		.dev_register =	snd_info_dev_register_entry,
904746d4a02STakashi Iwai 		/* disconnect is done via snd_info_card_disconnect() */
9051da177e4SLinus Torvalds 	};
90624c1f931STakashi Iwai 	struct snd_info_entry *entry;
9071da177e4SLinus Torvalds 	int err;
9081da177e4SLinus Torvalds 
9091da177e4SLinus Torvalds 	entry = snd_info_create_card_entry(card, name, card->proc_root);
9101da177e4SLinus Torvalds 	if (! entry)
9111da177e4SLinus Torvalds 		return -ENOMEM;
9121da177e4SLinus Torvalds 	if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
9131da177e4SLinus Torvalds 		snd_info_free_entry(entry);
9141da177e4SLinus Torvalds 		return err;
9151da177e4SLinus Torvalds 	}
9161da177e4SLinus Torvalds 	if (entryp)
9171da177e4SLinus Torvalds 		*entryp = entry;
9181da177e4SLinus Torvalds 	return 0;
9191da177e4SLinus Torvalds }
9201da177e4SLinus Torvalds 
921c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_proc_new);
922c0d3fb39STakashi Iwai 
9231da177e4SLinus Torvalds /**
9241da177e4SLinus Torvalds  * snd_info_free_entry - release the info entry
9251da177e4SLinus Torvalds  * @entry: the info entry
9261da177e4SLinus Torvalds  *
9271da177e4SLinus Torvalds  * Releases the info entry.  Don't call this after registered.
9281da177e4SLinus Torvalds  */
92924c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry)
9301da177e4SLinus Torvalds {
9311da177e4SLinus Torvalds 	if (entry == NULL)
9321da177e4SLinus Torvalds 		return;
933746d4a02STakashi Iwai 	if (entry->p) {
934746d4a02STakashi Iwai 		mutex_lock(&info_mutex);
935746d4a02STakashi Iwai 		snd_info_disconnect(entry);
936746d4a02STakashi Iwai 		mutex_unlock(&info_mutex);
937746d4a02STakashi Iwai 	}
9381da177e4SLinus Torvalds 	kfree(entry->name);
9391da177e4SLinus Torvalds 	if (entry->private_free)
9401da177e4SLinus Torvalds 		entry->private_free(entry);
9411da177e4SLinus Torvalds 	kfree(entry);
9421da177e4SLinus Torvalds }
9431da177e4SLinus Torvalds 
944c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry);
945c0d3fb39STakashi Iwai 
9461da177e4SLinus Torvalds /**
9471da177e4SLinus Torvalds  * snd_info_register - register the info entry
9481da177e4SLinus Torvalds  * @entry: the info entry
9491da177e4SLinus Torvalds  *
9501da177e4SLinus Torvalds  * Registers the proc info entry.
9511da177e4SLinus Torvalds  *
9521da177e4SLinus Torvalds  * Returns zero if successful, or a negative error code on failure.
9531da177e4SLinus Torvalds  */
95424c1f931STakashi Iwai int snd_info_register(struct snd_info_entry * entry)
9551da177e4SLinus Torvalds {
9561da177e4SLinus Torvalds 	struct proc_dir_entry *root, *p = NULL;
9571da177e4SLinus Torvalds 
9587eaa943cSTakashi Iwai 	if (snd_BUG_ON(!entry))
9597eaa943cSTakashi Iwai 		return -ENXIO;
9601da177e4SLinus Torvalds 	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
9611a60d4c5SIngo Molnar 	mutex_lock(&info_mutex);
962aee0c612SAl Viro 	if (S_ISDIR(entry->mode)) {
963aee0c612SAl Viro 		p = proc_mkdir_mode(entry->name, entry->mode, root);
9641da177e4SLinus Torvalds 		if (!p) {
9651a60d4c5SIngo Molnar 			mutex_unlock(&info_mutex);
9661da177e4SLinus Torvalds 			return -ENOMEM;
9671da177e4SLinus Torvalds 		}
968aee0c612SAl Viro 	} else {
969aee0c612SAl Viro 		p = proc_create_data(entry->name, entry->mode, root,
970aee0c612SAl Viro 					&snd_info_entry_operations, entry);
971aee0c612SAl Viro 		if (!p) {
972aee0c612SAl Viro 			mutex_unlock(&info_mutex);
973aee0c612SAl Viro 			return -ENOMEM;
974aee0c612SAl Viro 		}
9751da177e4SLinus Torvalds 		p->size = entry->size;
976aee0c612SAl Viro 	}
9771da177e4SLinus Torvalds 	entry->p = p;
978746d4a02STakashi Iwai 	if (entry->parent)
979746d4a02STakashi Iwai 		list_add_tail(&entry->list, &entry->parent->children);
9801a60d4c5SIngo Molnar 	mutex_unlock(&info_mutex);
9811da177e4SLinus Torvalds 	return 0;
9821da177e4SLinus Torvalds }
9831da177e4SLinus Torvalds 
984c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register);
985c0d3fb39STakashi Iwai 
9861da177e4SLinus Torvalds /*
9871da177e4SLinus Torvalds 
9881da177e4SLinus Torvalds  */
9891da177e4SLinus Torvalds 
9906581f4e7STakashi Iwai static struct snd_info_entry *snd_info_version_entry;
9911da177e4SLinus Torvalds 
99224c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
9931da177e4SLinus Torvalds {
9941da177e4SLinus Torvalds 	snd_iprintf(buffer,
99542662748SJaroslav Kysela 		    "Advanced Linux Sound Architecture Driver Version k%s.\n",
99642662748SJaroslav Kysela 		    init_utsname()->release);
9971da177e4SLinus Torvalds }
9981da177e4SLinus Torvalds 
9991da177e4SLinus Torvalds static int __init snd_info_version_init(void)
10001da177e4SLinus Torvalds {
100124c1f931STakashi Iwai 	struct snd_info_entry *entry;
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds 	entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
10041da177e4SLinus Torvalds 	if (entry == NULL)
10051da177e4SLinus Torvalds 		return -ENOMEM;
10061da177e4SLinus Torvalds 	entry->c.text.read = snd_info_version_read;
10071da177e4SLinus Torvalds 	if (snd_info_register(entry) < 0) {
10081da177e4SLinus Torvalds 		snd_info_free_entry(entry);
10091da177e4SLinus Torvalds 		return -ENOMEM;
10101da177e4SLinus Torvalds 	}
10111da177e4SLinus Torvalds 	snd_info_version_entry = entry;
10121da177e4SLinus Torvalds 	return 0;
10131da177e4SLinus Torvalds }
10141da177e4SLinus Torvalds 
10151da177e4SLinus Torvalds static int __exit snd_info_version_done(void)
10161da177e4SLinus Torvalds {
1017746d4a02STakashi Iwai 	snd_info_free_entry(snd_info_version_entry);
10181da177e4SLinus Torvalds 	return 0;
10191da177e4SLinus Torvalds }
10201da177e4SLinus Torvalds 
10211da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */
1022