xref: /openbmc/linux/sound/core/info.c (revision 7bc56323)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Information interface for ALSA driver
31da177e4SLinus Torvalds  *  Copyright (c) by Jaroslav Kysela <perex@suse.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 <sound/driver.h>
231da177e4SLinus Torvalds #include <linux/init.h>
241da177e4SLinus Torvalds #include <linux/time.h>
251da177e4SLinus Torvalds #include <linux/smp_lock.h>
26543537bdSPaulo Marques #include <linux/string.h>
271da177e4SLinus Torvalds #include <sound/core.h>
281da177e4SLinus Torvalds #include <sound/minors.h>
291da177e4SLinus Torvalds #include <sound/info.h>
301da177e4SLinus Torvalds #include <sound/version.h>
311da177e4SLinus Torvalds #include <linux/proc_fs.h>
321a60d4c5SIngo Molnar #include <linux/mutex.h>
331da177e4SLinus Torvalds #include <stdarg.h>
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds /*
361da177e4SLinus Torvalds  *
371da177e4SLinus Torvalds  */
381da177e4SLinus Torvalds 
39e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS
40e28563ccSTakashi Iwai 
411da177e4SLinus Torvalds int snd_info_check_reserved_words(const char *str)
421da177e4SLinus Torvalds {
431da177e4SLinus Torvalds 	static char *reserved[] =
441da177e4SLinus Torvalds 	{
451da177e4SLinus Torvalds 		"version",
461da177e4SLinus Torvalds 		"meminfo",
471da177e4SLinus Torvalds 		"memdebug",
481da177e4SLinus Torvalds 		"detect",
491da177e4SLinus Torvalds 		"devices",
501da177e4SLinus Torvalds 		"oss",
511da177e4SLinus Torvalds 		"cards",
521da177e4SLinus Torvalds 		"timers",
531da177e4SLinus Torvalds 		"synth",
541da177e4SLinus Torvalds 		"pcm",
551da177e4SLinus Torvalds 		"seq",
561da177e4SLinus Torvalds 		NULL
571da177e4SLinus Torvalds 	};
581da177e4SLinus Torvalds 	char **xstr = reserved;
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	while (*xstr) {
611da177e4SLinus Torvalds 		if (!strcmp(*xstr, str))
621da177e4SLinus Torvalds 			return 0;
631da177e4SLinus Torvalds 		xstr++;
641da177e4SLinus Torvalds 	}
651da177e4SLinus Torvalds 	if (!strncmp(str, "card", 4))
661da177e4SLinus Torvalds 		return 0;
671da177e4SLinus Torvalds 	return 1;
681da177e4SLinus Torvalds }
691da177e4SLinus Torvalds 
701a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex);
711da177e4SLinus Torvalds 
7224c1f931STakashi Iwai struct snd_info_private_data {
7324c1f931STakashi Iwai 	struct snd_info_buffer *rbuffer;
7424c1f931STakashi Iwai 	struct snd_info_buffer *wbuffer;
7524c1f931STakashi Iwai 	struct snd_info_entry *entry;
761da177e4SLinus Torvalds 	void *file_private_data;
7724c1f931STakashi Iwai };
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds static int snd_info_version_init(void);
801da177e4SLinus Torvalds static int snd_info_version_done(void);
81746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry);
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 
847e4eeec8STakashi Iwai /* resize the proc r/w buffer */
857e4eeec8STakashi Iwai static int resize_info_buffer(struct snd_info_buffer *buffer,
867e4eeec8STakashi Iwai 			      unsigned int nsize)
877e4eeec8STakashi Iwai {
887e4eeec8STakashi Iwai 	char *nbuf;
897e4eeec8STakashi Iwai 
907e4eeec8STakashi Iwai 	nsize = PAGE_ALIGN(nsize);
917e4eeec8STakashi Iwai 	nbuf = kmalloc(nsize, GFP_KERNEL);
927e4eeec8STakashi Iwai 	if (! nbuf)
937e4eeec8STakashi Iwai 		return -ENOMEM;
947e4eeec8STakashi Iwai 
957e4eeec8STakashi Iwai 	memcpy(nbuf, buffer->buffer, buffer->len);
967e4eeec8STakashi Iwai 	kfree(buffer->buffer);
977e4eeec8STakashi Iwai 	buffer->buffer = nbuf;
987e4eeec8STakashi Iwai 	buffer->len = nsize;
997e4eeec8STakashi Iwai 	return 0;
1007e4eeec8STakashi Iwai }
1017e4eeec8STakashi Iwai 
1021da177e4SLinus Torvalds /**
1031da177e4SLinus Torvalds  * snd_iprintf - printf on the procfs buffer
1041da177e4SLinus Torvalds  * @buffer: the procfs buffer
1051da177e4SLinus Torvalds  * @fmt: the printf format
1061da177e4SLinus Torvalds  *
1071da177e4SLinus Torvalds  * Outputs the string on the procfs buffer just like printf().
1081da177e4SLinus Torvalds  *
1091da177e4SLinus Torvalds  * Returns the size of output string.
1101da177e4SLinus Torvalds  */
11124c1f931STakashi Iwai int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...)
1121da177e4SLinus Torvalds {
1131da177e4SLinus Torvalds 	va_list args;
1141da177e4SLinus Torvalds 	int len, res;
1157e4eeec8STakashi Iwai 	int err = 0;
1161da177e4SLinus Torvalds 
117f001c3acSTakashi Iwai 	might_sleep();
1181da177e4SLinus Torvalds 	if (buffer->stop || buffer->error)
1191da177e4SLinus Torvalds 		return 0;
1201da177e4SLinus Torvalds 	len = buffer->len - buffer->size;
1211da177e4SLinus Torvalds 	va_start(args, fmt);
1227e4eeec8STakashi Iwai 	for (;;) {
123dbedca39STakashi Iwai 		va_list ap;
124dbedca39STakashi Iwai 		va_copy(ap, args);
125dbedca39STakashi Iwai 		res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap);
126dbedca39STakashi Iwai 		va_end(ap);
1277e4eeec8STakashi Iwai 		if (res < len)
1287e4eeec8STakashi Iwai 			break;
1297e4eeec8STakashi Iwai 		err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
1307e4eeec8STakashi Iwai 		if (err < 0)
1317e4eeec8STakashi Iwai 			break;
1327e4eeec8STakashi Iwai 		len = buffer->len - buffer->size;
1331da177e4SLinus Torvalds 	}
1347e4eeec8STakashi Iwai 	va_end(args);
1357e4eeec8STakashi Iwai 
1367e4eeec8STakashi Iwai 	if (err < 0)
1377e4eeec8STakashi Iwai 		return err;
1381da177e4SLinus Torvalds 	buffer->curr += res;
1391da177e4SLinus Torvalds 	buffer->size += res;
1401da177e4SLinus Torvalds 	return res;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
143c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_iprintf);
144c0d3fb39STakashi Iwai 
1451da177e4SLinus Torvalds /*
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds  */
1481da177e4SLinus Torvalds 
1496581f4e7STakashi Iwai static struct proc_dir_entry *snd_proc_root;
1506581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root;
151c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root);
152c0d3fb39STakashi Iwai 
1531da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
1546581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root;
1551da177e4SLinus Torvalds #endif
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
1581da177e4SLinus Torvalds {
1591da177e4SLinus Torvalds 	de->owner = THIS_MODULE;
1601da177e4SLinus Torvalds }
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds static void snd_remove_proc_entry(struct proc_dir_entry *parent,
1631da177e4SLinus Torvalds 				  struct proc_dir_entry *de)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds 	if (de)
1661da177e4SLinus Torvalds 		remove_proc_entry(de->name, parent);
1671da177e4SLinus Torvalds }
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
1701da177e4SLinus Torvalds {
17124c1f931STakashi Iwai 	struct snd_info_private_data *data;
1721da177e4SLinus Torvalds 	struct snd_info_entry *entry;
1731da177e4SLinus Torvalds 	loff_t ret;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	data = file->private_data;
1761da177e4SLinus Torvalds 	entry = data->entry;
1771da177e4SLinus Torvalds 	lock_kernel();
1781da177e4SLinus Torvalds 	switch (entry->content) {
1791da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_TEXT:
1801da177e4SLinus Torvalds 		switch (orig) {
181e6f8f108SJosef 'Jeff' Sipek 		case SEEK_SET:
1821da177e4SLinus Torvalds 			file->f_pos = offset;
1831da177e4SLinus Torvalds 			ret = file->f_pos;
1841da177e4SLinus Torvalds 			goto out;
185e6f8f108SJosef 'Jeff' Sipek 		case SEEK_CUR:
1861da177e4SLinus Torvalds 			file->f_pos += offset;
1871da177e4SLinus Torvalds 			ret = file->f_pos;
1881da177e4SLinus Torvalds 			goto out;
189e6f8f108SJosef 'Jeff' Sipek 		case SEEK_END:
1901da177e4SLinus Torvalds 		default:
1911da177e4SLinus Torvalds 			ret = -EINVAL;
1921da177e4SLinus Torvalds 			goto out;
1931da177e4SLinus Torvalds 		}
1941da177e4SLinus Torvalds 		break;
1951da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
1961da177e4SLinus Torvalds 		if (entry->c.ops->llseek) {
1971da177e4SLinus Torvalds 			ret = entry->c.ops->llseek(entry,
1981da177e4SLinus Torvalds 						    data->file_private_data,
1991da177e4SLinus Torvalds 						    file, offset, orig);
2001da177e4SLinus Torvalds 			goto out;
2011da177e4SLinus Torvalds 		}
2021da177e4SLinus Torvalds 		break;
2031da177e4SLinus Torvalds 	}
2041da177e4SLinus Torvalds 	ret = -ENXIO;
2051da177e4SLinus Torvalds out:
2061da177e4SLinus Torvalds 	unlock_kernel();
2071da177e4SLinus Torvalds 	return ret;
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
2111da177e4SLinus Torvalds 				   size_t count, loff_t * offset)
2121da177e4SLinus Torvalds {
21324c1f931STakashi Iwai 	struct snd_info_private_data *data;
2141da177e4SLinus Torvalds 	struct snd_info_entry *entry;
21524c1f931STakashi Iwai 	struct snd_info_buffer *buf;
2161da177e4SLinus Torvalds 	size_t size = 0;
2171da177e4SLinus Torvalds 	loff_t pos;
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds 	data = file->private_data;
2201da177e4SLinus Torvalds 	snd_assert(data != NULL, return -ENXIO);
2211da177e4SLinus Torvalds 	pos = *offset;
2221da177e4SLinus Torvalds 	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
2231da177e4SLinus Torvalds 		return -EIO;
2241da177e4SLinus Torvalds 	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
2251da177e4SLinus Torvalds 		return -EIO;
2261da177e4SLinus Torvalds 	entry = data->entry;
2271da177e4SLinus Torvalds 	switch (entry->content) {
2281da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_TEXT:
2291da177e4SLinus Torvalds 		buf = data->rbuffer;
2301da177e4SLinus Torvalds 		if (buf == NULL)
2311da177e4SLinus Torvalds 			return -EIO;
2321da177e4SLinus Torvalds 		if (pos >= buf->size)
2331da177e4SLinus Torvalds 			return 0;
2341da177e4SLinus Torvalds 		size = buf->size - pos;
2351da177e4SLinus Torvalds 		size = min(count, size);
2361da177e4SLinus Torvalds 		if (copy_to_user(buffer, buf->buffer + pos, size))
2371da177e4SLinus Torvalds 			return -EFAULT;
2381da177e4SLinus Torvalds 		break;
2391da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
2401da177e4SLinus Torvalds 		if (entry->c.ops->read)
2411da177e4SLinus Torvalds 			size = entry->c.ops->read(entry,
2421da177e4SLinus Torvalds 						  data->file_private_data,
2431da177e4SLinus Torvalds 						  file, buffer, count, pos);
2441da177e4SLinus Torvalds 		break;
2451da177e4SLinus Torvalds 	}
2461da177e4SLinus Torvalds 	if ((ssize_t) size > 0)
2471da177e4SLinus Torvalds 		*offset = pos + size;
2481da177e4SLinus Torvalds 	return size;
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
2521da177e4SLinus Torvalds 				    size_t count, loff_t * offset)
2531da177e4SLinus Torvalds {
25424c1f931STakashi Iwai 	struct snd_info_private_data *data;
2551da177e4SLinus Torvalds 	struct snd_info_entry *entry;
25624c1f931STakashi Iwai 	struct snd_info_buffer *buf;
2577e4eeec8STakashi Iwai 	ssize_t size = 0;
2581da177e4SLinus Torvalds 	loff_t pos;
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds 	data = file->private_data;
2611da177e4SLinus Torvalds 	snd_assert(data != NULL, return -ENXIO);
2621da177e4SLinus Torvalds 	entry = data->entry;
2631da177e4SLinus Torvalds 	pos = *offset;
2641da177e4SLinus Torvalds 	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
2651da177e4SLinus Torvalds 		return -EIO;
2661da177e4SLinus Torvalds 	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
2671da177e4SLinus Torvalds 		return -EIO;
2681da177e4SLinus Torvalds 	switch (entry->content) {
2691da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_TEXT:
2701da177e4SLinus Torvalds 		buf = data->wbuffer;
2711da177e4SLinus Torvalds 		if (buf == NULL)
2721da177e4SLinus Torvalds 			return -EIO;
273f4a747f1SClemens Ladisch 		mutex_lock(&entry->access);
2747e4eeec8STakashi Iwai 		if (pos + count >= buf->len) {
2757e4eeec8STakashi Iwai 			if (resize_info_buffer(buf, pos + count)) {
2767e4eeec8STakashi Iwai 				mutex_unlock(&entry->access);
2771da177e4SLinus Torvalds 				return -ENOMEM;
2787e4eeec8STakashi Iwai 			}
2797e4eeec8STakashi Iwai 		}
2807e4eeec8STakashi Iwai 		if (copy_from_user(buf->buffer + pos, buffer, count)) {
2817e4eeec8STakashi Iwai 			mutex_unlock(&entry->access);
2821da177e4SLinus Torvalds 			return -EFAULT;
2837e4eeec8STakashi Iwai 		}
2847e4eeec8STakashi Iwai 		buf->size = pos + count;
2857e4eeec8STakashi Iwai 		mutex_unlock(&entry->access);
2867e4eeec8STakashi Iwai 		size = count;
2871da177e4SLinus Torvalds 		break;
2881da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
2891da177e4SLinus Torvalds 		if (entry->c.ops->write)
2901da177e4SLinus Torvalds 			size = entry->c.ops->write(entry,
2911da177e4SLinus Torvalds 						   data->file_private_data,
2921da177e4SLinus Torvalds 						   file, buffer, count, pos);
2931da177e4SLinus Torvalds 		break;
2941da177e4SLinus Torvalds 	}
2951da177e4SLinus Torvalds 	if ((ssize_t) size > 0)
2961da177e4SLinus Torvalds 		*offset = pos + size;
2971da177e4SLinus Torvalds 	return size;
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds static int snd_info_entry_open(struct inode *inode, struct file *file)
3011da177e4SLinus Torvalds {
30224c1f931STakashi Iwai 	struct snd_info_entry *entry;
30324c1f931STakashi Iwai 	struct snd_info_private_data *data;
30424c1f931STakashi Iwai 	struct snd_info_buffer *buffer;
3051da177e4SLinus Torvalds 	struct proc_dir_entry *p;
3061da177e4SLinus Torvalds 	int mode, err;
3071da177e4SLinus Torvalds 
3081a60d4c5SIngo Molnar 	mutex_lock(&info_mutex);
3091da177e4SLinus Torvalds 	p = PDE(inode);
31024c1f931STakashi Iwai 	entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
311746d4a02STakashi Iwai 	if (entry == NULL || ! entry->p) {
3121a60d4c5SIngo Molnar 		mutex_unlock(&info_mutex);
3131da177e4SLinus Torvalds 		return -ENODEV;
3141da177e4SLinus Torvalds 	}
3151da177e4SLinus Torvalds 	if (!try_module_get(entry->module)) {
3161da177e4SLinus Torvalds 		err = -EFAULT;
3171da177e4SLinus Torvalds 		goto __error1;
3181da177e4SLinus Torvalds 	}
3191da177e4SLinus Torvalds 	mode = file->f_flags & O_ACCMODE;
3201da177e4SLinus Torvalds 	if (mode == O_RDONLY || mode == O_RDWR) {
3217e4eeec8STakashi Iwai 		if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
3221da177e4SLinus Torvalds 		     entry->c.ops->read == NULL)) {
3231da177e4SLinus Torvalds 		    	err = -ENODEV;
3241da177e4SLinus Torvalds 		    	goto __error;
3251da177e4SLinus Torvalds 		}
3261da177e4SLinus Torvalds 	}
3271da177e4SLinus Torvalds 	if (mode == O_WRONLY || mode == O_RDWR) {
3287e4eeec8STakashi Iwai 		if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
3291da177e4SLinus Torvalds 		     entry->c.ops->write == NULL)) {
3301da177e4SLinus Torvalds 		    	err = -ENODEV;
3311da177e4SLinus Torvalds 		    	goto __error;
3321da177e4SLinus Torvalds 		}
3331da177e4SLinus Torvalds 	}
334ca2c0966STakashi Iwai 	data = kzalloc(sizeof(*data), GFP_KERNEL);
3351da177e4SLinus Torvalds 	if (data == NULL) {
3361da177e4SLinus Torvalds 		err = -ENOMEM;
3371da177e4SLinus Torvalds 		goto __error;
3381da177e4SLinus Torvalds 	}
3391da177e4SLinus Torvalds 	data->entry = entry;
3401da177e4SLinus Torvalds 	switch (entry->content) {
3411da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_TEXT:
3421da177e4SLinus Torvalds 		if (mode == O_RDONLY || mode == O_RDWR) {
343ca2c0966STakashi Iwai 			buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
3447e4eeec8STakashi Iwai 			if (buffer == NULL)
3457e4eeec8STakashi Iwai 				goto __nomem;
3461da177e4SLinus Torvalds 			data->rbuffer = buffer;
3477e4eeec8STakashi Iwai 			buffer->len = PAGE_SIZE;
3487e4eeec8STakashi Iwai 			buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
3497e4eeec8STakashi Iwai 			if (buffer->buffer == NULL)
3507e4eeec8STakashi Iwai 				goto __nomem;
3511da177e4SLinus Torvalds 		}
3521da177e4SLinus Torvalds 		if (mode == O_WRONLY || mode == O_RDWR) {
353ca2c0966STakashi Iwai 			buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
3547e4eeec8STakashi Iwai 			if (buffer == NULL)
3557e4eeec8STakashi Iwai 				goto __nomem;
3561da177e4SLinus Torvalds 			data->wbuffer = buffer;
3577e4eeec8STakashi Iwai 			buffer->len = PAGE_SIZE;
3587e4eeec8STakashi Iwai 			buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
3597e4eeec8STakashi Iwai 			if (buffer->buffer == NULL)
3607e4eeec8STakashi Iwai 				goto __nomem;
3611da177e4SLinus Torvalds 		}
3621da177e4SLinus Torvalds 		break;
3631da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:	/* data */
3641da177e4SLinus Torvalds 		if (entry->c.ops->open) {
3651da177e4SLinus Torvalds 			if ((err = entry->c.ops->open(entry, mode,
3661da177e4SLinus Torvalds 						      &data->file_private_data)) < 0) {
3671da177e4SLinus Torvalds 				kfree(data);
3681da177e4SLinus Torvalds 				goto __error;
3691da177e4SLinus Torvalds 			}
3701da177e4SLinus Torvalds 		}
3711da177e4SLinus Torvalds 		break;
3721da177e4SLinus Torvalds 	}
3731da177e4SLinus Torvalds 	file->private_data = data;
3741a60d4c5SIngo Molnar 	mutex_unlock(&info_mutex);
3751da177e4SLinus Torvalds 	if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
3761da177e4SLinus Torvalds 	    (mode == O_RDONLY || mode == O_RDWR)) {
3771da177e4SLinus Torvalds 		if (entry->c.text.read) {
3781a60d4c5SIngo Molnar 			mutex_lock(&entry->access);
3791da177e4SLinus Torvalds 			entry->c.text.read(entry, data->rbuffer);
3801a60d4c5SIngo Molnar 			mutex_unlock(&entry->access);
3811da177e4SLinus Torvalds 		}
3821da177e4SLinus Torvalds 	}
3831da177e4SLinus Torvalds 	return 0;
3841da177e4SLinus Torvalds 
3857e4eeec8STakashi Iwai  __nomem:
3867e4eeec8STakashi Iwai 	if (data->rbuffer) {
3877e4eeec8STakashi Iwai 		kfree(data->rbuffer->buffer);
3887e4eeec8STakashi Iwai 		kfree(data->rbuffer);
3897e4eeec8STakashi Iwai 	}
3907e4eeec8STakashi Iwai 	if (data->wbuffer) {
3917e4eeec8STakashi Iwai 		kfree(data->wbuffer->buffer);
3927e4eeec8STakashi Iwai 		kfree(data->wbuffer);
3937e4eeec8STakashi Iwai 	}
3947e4eeec8STakashi Iwai 	kfree(data);
3957e4eeec8STakashi Iwai 	err = -ENOMEM;
3961da177e4SLinus Torvalds       __error:
3971da177e4SLinus Torvalds 	module_put(entry->module);
3981da177e4SLinus Torvalds       __error1:
3991a60d4c5SIngo Molnar 	mutex_unlock(&info_mutex);
4001da177e4SLinus Torvalds 	return err;
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds static int snd_info_entry_release(struct inode *inode, struct file *file)
4041da177e4SLinus Torvalds {
40524c1f931STakashi Iwai 	struct snd_info_entry *entry;
40624c1f931STakashi Iwai 	struct snd_info_private_data *data;
4071da177e4SLinus Torvalds 	int mode;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	mode = file->f_flags & O_ACCMODE;
4101da177e4SLinus Torvalds 	data = file->private_data;
4111da177e4SLinus Torvalds 	entry = data->entry;
4121da177e4SLinus Torvalds 	switch (entry->content) {
4131da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_TEXT:
4147e4eeec8STakashi Iwai 		if (data->rbuffer) {
4157e4eeec8STakashi Iwai 			kfree(data->rbuffer->buffer);
4161da177e4SLinus Torvalds 			kfree(data->rbuffer);
4171da177e4SLinus Torvalds 		}
4187e4eeec8STakashi Iwai 		if (data->wbuffer) {
4191da177e4SLinus Torvalds 			if (entry->c.text.write) {
4201da177e4SLinus Torvalds 				entry->c.text.write(entry, data->wbuffer);
4211da177e4SLinus Torvalds 				if (data->wbuffer->error) {
4221da177e4SLinus Torvalds 					snd_printk(KERN_WARNING "data write error to %s (%i)\n",
4231da177e4SLinus Torvalds 						entry->name,
4241da177e4SLinus Torvalds 						data->wbuffer->error);
4251da177e4SLinus Torvalds 				}
4261da177e4SLinus Torvalds 			}
4277e4eeec8STakashi Iwai 			kfree(data->wbuffer->buffer);
4281da177e4SLinus Torvalds 			kfree(data->wbuffer);
4291da177e4SLinus Torvalds 		}
4301da177e4SLinus Torvalds 		break;
4311da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
4321da177e4SLinus Torvalds 		if (entry->c.ops->release)
4331da177e4SLinus Torvalds 			entry->c.ops->release(entry, mode,
4341da177e4SLinus Torvalds 					      data->file_private_data);
4351da177e4SLinus Torvalds 		break;
4361da177e4SLinus Torvalds 	}
4371da177e4SLinus Torvalds 	module_put(entry->module);
4381da177e4SLinus Torvalds 	kfree(data);
4391da177e4SLinus Torvalds 	return 0;
4401da177e4SLinus Torvalds }
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
4431da177e4SLinus Torvalds {
44424c1f931STakashi Iwai 	struct snd_info_private_data *data;
4451da177e4SLinus Torvalds 	struct snd_info_entry *entry;
4461da177e4SLinus Torvalds 	unsigned int mask;
4471da177e4SLinus Torvalds 
4481da177e4SLinus Torvalds 	data = file->private_data;
4491da177e4SLinus Torvalds 	if (data == NULL)
4501da177e4SLinus Torvalds 		return 0;
4511da177e4SLinus Torvalds 	entry = data->entry;
4521da177e4SLinus Torvalds 	mask = 0;
4531da177e4SLinus Torvalds 	switch (entry->content) {
4541da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
4551da177e4SLinus Torvalds 		if (entry->c.ops->poll)
4561da177e4SLinus Torvalds 			return entry->c.ops->poll(entry,
4571da177e4SLinus Torvalds 						  data->file_private_data,
4581da177e4SLinus Torvalds 						  file, wait);
4591da177e4SLinus Torvalds 		if (entry->c.ops->read)
4601da177e4SLinus Torvalds 			mask |= POLLIN | POLLRDNORM;
4611da177e4SLinus Torvalds 		if (entry->c.ops->write)
4621da177e4SLinus Torvalds 			mask |= POLLOUT | POLLWRNORM;
4631da177e4SLinus Torvalds 		break;
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds 	return mask;
4661da177e4SLinus Torvalds }
4671da177e4SLinus Torvalds 
468d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
469d99e9889SIngo Molnar 				unsigned long arg)
4701da177e4SLinus Torvalds {
47124c1f931STakashi Iwai 	struct snd_info_private_data *data;
4721da177e4SLinus Torvalds 	struct snd_info_entry *entry;
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	data = file->private_data;
4751da177e4SLinus Torvalds 	if (data == NULL)
4761da177e4SLinus Torvalds 		return 0;
4771da177e4SLinus Torvalds 	entry = data->entry;
4781da177e4SLinus Torvalds 	switch (entry->content) {
4791da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
4801da177e4SLinus Torvalds 		if (entry->c.ops->ioctl)
4811da177e4SLinus Torvalds 			return entry->c.ops->ioctl(entry,
4821da177e4SLinus Torvalds 						   data->file_private_data,
4831da177e4SLinus Torvalds 						   file, cmd, arg);
4841da177e4SLinus Torvalds 		break;
4851da177e4SLinus Torvalds 	}
4861da177e4SLinus Torvalds 	return -ENOTTY;
4871da177e4SLinus Torvalds }
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
4901da177e4SLinus Torvalds {
4917bc56323SJosef Sipek 	struct inode *inode = file->f_path.dentry->d_inode;
49224c1f931STakashi Iwai 	struct snd_info_private_data *data;
4931da177e4SLinus Torvalds 	struct snd_info_entry *entry;
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds 	data = file->private_data;
4961da177e4SLinus Torvalds 	if (data == NULL)
4971da177e4SLinus Torvalds 		return 0;
4981da177e4SLinus Torvalds 	entry = data->entry;
4991da177e4SLinus Torvalds 	switch (entry->content) {
5001da177e4SLinus Torvalds 	case SNDRV_INFO_CONTENT_DATA:
5011da177e4SLinus Torvalds 		if (entry->c.ops->mmap)
5021da177e4SLinus Torvalds 			return entry->c.ops->mmap(entry,
5031da177e4SLinus Torvalds 						  data->file_private_data,
5041da177e4SLinus Torvalds 						  inode, file, vma);
5051da177e4SLinus Torvalds 		break;
5061da177e4SLinus Torvalds 	}
5071da177e4SLinus Torvalds 	return -ENXIO;
5081da177e4SLinus Torvalds }
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds static struct file_operations snd_info_entry_operations =
5111da177e4SLinus Torvalds {
5121da177e4SLinus Torvalds 	.owner =		THIS_MODULE,
5131da177e4SLinus Torvalds 	.llseek =		snd_info_entry_llseek,
5141da177e4SLinus Torvalds 	.read =			snd_info_entry_read,
5151da177e4SLinus Torvalds 	.write =		snd_info_entry_write,
5161da177e4SLinus Torvalds 	.poll =			snd_info_entry_poll,
517d99e9889SIngo Molnar 	.unlocked_ioctl =	snd_info_entry_ioctl,
5181da177e4SLinus Torvalds 	.mmap =			snd_info_entry_mmap,
5191da177e4SLinus Torvalds 	.open =			snd_info_entry_open,
5201da177e4SLinus Torvalds 	.release =		snd_info_entry_release,
5211da177e4SLinus Torvalds };
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds /**
5241da177e4SLinus Torvalds  * snd_create_proc_entry - create a procfs entry
5251da177e4SLinus Torvalds  * @name: the name of the proc file
5261da177e4SLinus Torvalds  * @mode: the file permission bits, S_Ixxx
5271da177e4SLinus Torvalds  * @parent: the parent proc-directory entry
5281da177e4SLinus Torvalds  *
5291da177e4SLinus Torvalds  * Creates a new proc file entry with the given name and permission
5301da177e4SLinus Torvalds  * on the given directory.
5311da177e4SLinus Torvalds  *
5321da177e4SLinus Torvalds  * Returns the pointer of new instance or NULL on failure.
5331da177e4SLinus Torvalds  */
5341da177e4SLinus Torvalds static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
5351da177e4SLinus Torvalds 						    struct proc_dir_entry *parent)
5361da177e4SLinus Torvalds {
5371da177e4SLinus Torvalds 	struct proc_dir_entry *p;
5381da177e4SLinus Torvalds 	p = create_proc_entry(name, mode, parent);
5391da177e4SLinus Torvalds 	if (p)
5401da177e4SLinus Torvalds 		snd_info_entry_prepare(p);
5411da177e4SLinus Torvalds 	return p;
5421da177e4SLinus Torvalds }
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds int __init snd_info_init(void)
5451da177e4SLinus Torvalds {
5461da177e4SLinus Torvalds 	struct proc_dir_entry *p;
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root);
5491da177e4SLinus Torvalds 	if (p == NULL)
5501da177e4SLinus Torvalds 		return -ENOMEM;
5511da177e4SLinus Torvalds 	snd_proc_root = p;
5521da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
5531da177e4SLinus Torvalds 	{
55424c1f931STakashi Iwai 		struct snd_info_entry *entry;
5551da177e4SLinus Torvalds 		if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", 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_oss_root = entry;
5631da177e4SLinus Torvalds 	}
5641da177e4SLinus Torvalds #endif
5651da177e4SLinus Torvalds #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
5661da177e4SLinus Torvalds 	{
56724c1f931STakashi Iwai 		struct snd_info_entry *entry;
5681da177e4SLinus Torvalds 		if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
5691da177e4SLinus Torvalds 			return -ENOMEM;
5701da177e4SLinus Torvalds 		entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
5711da177e4SLinus Torvalds 		if (snd_info_register(entry) < 0) {
5721da177e4SLinus Torvalds 			snd_info_free_entry(entry);
5731da177e4SLinus Torvalds 			return -ENOMEM;
5741da177e4SLinus Torvalds 		}
5751da177e4SLinus Torvalds 		snd_seq_root = entry;
5761da177e4SLinus Torvalds 	}
5771da177e4SLinus Torvalds #endif
5781da177e4SLinus Torvalds 	snd_info_version_init();
5791da177e4SLinus Torvalds 	snd_minor_info_init();
5801da177e4SLinus Torvalds 	snd_minor_info_oss_init();
5811da177e4SLinus Torvalds 	snd_card_info_init();
5821da177e4SLinus Torvalds 	return 0;
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds int __exit snd_info_done(void)
5861da177e4SLinus Torvalds {
5871da177e4SLinus Torvalds 	snd_card_info_done();
5881da177e4SLinus Torvalds 	snd_minor_info_oss_done();
5891da177e4SLinus Torvalds 	snd_minor_info_done();
5901da177e4SLinus Torvalds 	snd_info_version_done();
5911da177e4SLinus Torvalds 	if (snd_proc_root) {
5921da177e4SLinus Torvalds #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
593746d4a02STakashi Iwai 		snd_info_free_entry(snd_seq_root);
5941da177e4SLinus Torvalds #endif
5951da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
596746d4a02STakashi Iwai 		snd_info_free_entry(snd_oss_root);
5971da177e4SLinus Torvalds #endif
5981da177e4SLinus Torvalds 		snd_remove_proc_entry(&proc_root, snd_proc_root);
5991da177e4SLinus Torvalds 	}
6001da177e4SLinus Torvalds 	return 0;
6011da177e4SLinus Torvalds }
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds /*
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds  */
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds /*
6091da177e4SLinus Torvalds  * create a card proc file
6101da177e4SLinus Torvalds  * called from init.c
6111da177e4SLinus Torvalds  */
61224c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card)
6131da177e4SLinus Torvalds {
6141da177e4SLinus Torvalds 	char str[8];
61524c1f931STakashi Iwai 	struct snd_info_entry *entry;
6161da177e4SLinus Torvalds 
6171da177e4SLinus Torvalds 	snd_assert(card != NULL, return -ENXIO);
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 	sprintf(str, "card%i", card->number);
6201da177e4SLinus Torvalds 	if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
6211da177e4SLinus Torvalds 		return -ENOMEM;
6221da177e4SLinus Torvalds 	entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
6231da177e4SLinus Torvalds 	if (snd_info_register(entry) < 0) {
6241da177e4SLinus Torvalds 		snd_info_free_entry(entry);
6251da177e4SLinus Torvalds 		return -ENOMEM;
6261da177e4SLinus Torvalds 	}
6271da177e4SLinus Torvalds 	card->proc_root = entry;
6281da177e4SLinus Torvalds 	return 0;
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds /*
6321da177e4SLinus Torvalds  * register the card proc file
6331da177e4SLinus Torvalds  * called from init.c
6341da177e4SLinus Torvalds  */
63524c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card)
6361da177e4SLinus Torvalds {
6371da177e4SLinus Torvalds 	struct proc_dir_entry *p;
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds 	snd_assert(card != NULL, return -ENXIO);
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	if (!strcmp(card->id, card->proc_root->name))
6421da177e4SLinus Torvalds 		return 0;
6431da177e4SLinus Torvalds 
6441da177e4SLinus Torvalds 	p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
6451da177e4SLinus Torvalds 	if (p == NULL)
6461da177e4SLinus Torvalds 		return -ENOMEM;
6471da177e4SLinus Torvalds 	card->proc_root_link = p;
6481da177e4SLinus Torvalds 	return 0;
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds 
6511da177e4SLinus Torvalds /*
6521da177e4SLinus Torvalds  * de-register the card proc file
6531da177e4SLinus Torvalds  * called from init.c
6541da177e4SLinus Torvalds  */
655746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card)
6561da177e4SLinus Torvalds {
657746d4a02STakashi Iwai 	snd_assert(card != NULL, return);
658746d4a02STakashi Iwai 	mutex_lock(&info_mutex);
6591da177e4SLinus Torvalds 	if (card->proc_root_link) {
6601da177e4SLinus Torvalds 		snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
6611da177e4SLinus Torvalds 		card->proc_root_link = NULL;
6621da177e4SLinus Torvalds 	}
663746d4a02STakashi Iwai 	if (card->proc_root)
664746d4a02STakashi Iwai 		snd_info_disconnect(card->proc_root);
665746d4a02STakashi Iwai 	mutex_unlock(&info_mutex);
6661da177e4SLinus Torvalds }
667746d4a02STakashi Iwai 
668746d4a02STakashi Iwai /*
669746d4a02STakashi Iwai  * release the card proc file resources
670746d4a02STakashi Iwai  * called from init.c
671746d4a02STakashi Iwai  */
672746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card)
673746d4a02STakashi Iwai {
674746d4a02STakashi Iwai 	snd_assert(card != NULL, return -ENXIO);
675746d4a02STakashi Iwai 	snd_info_free_entry(card->proc_root);
676746d4a02STakashi Iwai 	card->proc_root = NULL;
6771da177e4SLinus Torvalds 	return 0;
6781da177e4SLinus Torvalds }
6791da177e4SLinus Torvalds 
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds /**
6821da177e4SLinus Torvalds  * snd_info_get_line - read one line from the procfs buffer
6831da177e4SLinus Torvalds  * @buffer: the procfs buffer
6841da177e4SLinus Torvalds  * @line: the buffer to store
6851da177e4SLinus Torvalds  * @len: the max. buffer size - 1
6861da177e4SLinus Torvalds  *
6871da177e4SLinus Torvalds  * Reads one line from the buffer and stores the string.
6881da177e4SLinus Torvalds  *
6891da177e4SLinus Torvalds  * Returns zero if successful, or 1 if error or EOF.
6901da177e4SLinus Torvalds  */
69124c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
6921da177e4SLinus Torvalds {
6931da177e4SLinus Torvalds 	int c = -1;
6941da177e4SLinus Torvalds 
6951da177e4SLinus Torvalds 	if (len <= 0 || buffer->stop || buffer->error)
6961da177e4SLinus Torvalds 		return 1;
6971da177e4SLinus Torvalds 	while (--len > 0) {
6987e4eeec8STakashi Iwai 		c = buffer->buffer[buffer->curr++];
6991da177e4SLinus Torvalds 		if (c == '\n') {
7007e4eeec8STakashi Iwai 			if (buffer->curr >= buffer->size)
7011da177e4SLinus Torvalds 				buffer->stop = 1;
7021da177e4SLinus Torvalds 			break;
7031da177e4SLinus Torvalds 		}
7041da177e4SLinus Torvalds 		*line++ = c;
7057e4eeec8STakashi Iwai 		if (buffer->curr >= buffer->size) {
7061da177e4SLinus Torvalds 			buffer->stop = 1;
7071da177e4SLinus Torvalds 			break;
7081da177e4SLinus Torvalds 		}
7091da177e4SLinus Torvalds 	}
7101da177e4SLinus Torvalds 	while (c != '\n' && !buffer->stop) {
7117e4eeec8STakashi Iwai 		c = buffer->buffer[buffer->curr++];
7127e4eeec8STakashi Iwai 		if (buffer->curr >= buffer->size)
7131da177e4SLinus Torvalds 			buffer->stop = 1;
7141da177e4SLinus Torvalds 	}
7151da177e4SLinus Torvalds 	*line = '\0';
7161da177e4SLinus Torvalds 	return 0;
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds 
719c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line);
720c0d3fb39STakashi Iwai 
7211da177e4SLinus Torvalds /**
722856def8aSHenrik Kretzschmar  * snd_info_get_str - parse a string token
7231da177e4SLinus Torvalds  * @dest: the buffer to store the string token
7241da177e4SLinus Torvalds  * @src: the original string
7251da177e4SLinus Torvalds  * @len: the max. length of token - 1
7261da177e4SLinus Torvalds  *
7271da177e4SLinus Torvalds  * Parses the original string and copy a token to the given
7281da177e4SLinus Torvalds  * string buffer.
7291da177e4SLinus Torvalds  *
7301da177e4SLinus Torvalds  * Returns the updated pointer of the original string so that
7311da177e4SLinus Torvalds  * it can be used for the next call.
7321da177e4SLinus Torvalds  */
7331da177e4SLinus Torvalds char *snd_info_get_str(char *dest, char *src, int len)
7341da177e4SLinus Torvalds {
7351da177e4SLinus Torvalds 	int c;
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds 	while (*src == ' ' || *src == '\t')
7381da177e4SLinus Torvalds 		src++;
7391da177e4SLinus Torvalds 	if (*src == '"' || *src == '\'') {
7401da177e4SLinus Torvalds 		c = *src++;
7411da177e4SLinus Torvalds 		while (--len > 0 && *src && *src != c) {
7421da177e4SLinus Torvalds 			*dest++ = *src++;
7431da177e4SLinus Torvalds 		}
7441da177e4SLinus Torvalds 		if (*src == c)
7451da177e4SLinus Torvalds 			src++;
7461da177e4SLinus Torvalds 	} else {
7471da177e4SLinus Torvalds 		while (--len > 0 && *src && *src != ' ' && *src != '\t') {
7481da177e4SLinus Torvalds 			*dest++ = *src++;
7491da177e4SLinus Torvalds 		}
7501da177e4SLinus Torvalds 	}
7511da177e4SLinus Torvalds 	*dest = 0;
7521da177e4SLinus Torvalds 	while (*src == ' ' || *src == '\t')
7531da177e4SLinus Torvalds 		src++;
7541da177e4SLinus Torvalds 	return src;
7551da177e4SLinus Torvalds }
7561da177e4SLinus Torvalds 
757c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str);
758c0d3fb39STakashi Iwai 
7591da177e4SLinus Torvalds /**
7601da177e4SLinus Torvalds  * snd_info_create_entry - create an info entry
7611da177e4SLinus Torvalds  * @name: the proc file name
7621da177e4SLinus Torvalds  *
7631da177e4SLinus Torvalds  * Creates an info entry with the given file name and initializes as
7641da177e4SLinus Torvalds  * the default state.
7651da177e4SLinus Torvalds  *
7661da177e4SLinus Torvalds  * Usually called from other functions such as
7671da177e4SLinus Torvalds  * snd_info_create_card_entry().
7681da177e4SLinus Torvalds  *
7691da177e4SLinus Torvalds  * Returns the pointer of the new instance, or NULL on failure.
7701da177e4SLinus Torvalds  */
77124c1f931STakashi Iwai static struct snd_info_entry *snd_info_create_entry(const char *name)
7721da177e4SLinus Torvalds {
77324c1f931STakashi Iwai 	struct snd_info_entry *entry;
774ca2c0966STakashi Iwai 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
7751da177e4SLinus Torvalds 	if (entry == NULL)
7761da177e4SLinus Torvalds 		return NULL;
777543537bdSPaulo Marques 	entry->name = kstrdup(name, GFP_KERNEL);
7781da177e4SLinus Torvalds 	if (entry->name == NULL) {
7791da177e4SLinus Torvalds 		kfree(entry);
7801da177e4SLinus Torvalds 		return NULL;
7811da177e4SLinus Torvalds 	}
7821da177e4SLinus Torvalds 	entry->mode = S_IFREG | S_IRUGO;
7831da177e4SLinus Torvalds 	entry->content = SNDRV_INFO_CONTENT_TEXT;
7841a60d4c5SIngo Molnar 	mutex_init(&entry->access);
785746d4a02STakashi Iwai 	INIT_LIST_HEAD(&entry->children);
786746d4a02STakashi Iwai 	INIT_LIST_HEAD(&entry->list);
7871da177e4SLinus Torvalds 	return entry;
7881da177e4SLinus Torvalds }
7891da177e4SLinus Torvalds 
7901da177e4SLinus Torvalds /**
7911da177e4SLinus Torvalds  * snd_info_create_module_entry - create an info entry for the given module
7921da177e4SLinus Torvalds  * @module: the module pointer
7931da177e4SLinus Torvalds  * @name: the file name
7941da177e4SLinus Torvalds  * @parent: the parent directory
7951da177e4SLinus Torvalds  *
7961da177e4SLinus Torvalds  * Creates a new info entry and assigns it to the given module.
7971da177e4SLinus Torvalds  *
7981da177e4SLinus Torvalds  * Returns the pointer of the new instance, or NULL on failure.
7991da177e4SLinus Torvalds  */
80024c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module,
8011da177e4SLinus Torvalds 					       const char *name,
80224c1f931STakashi Iwai 					       struct snd_info_entry *parent)
8031da177e4SLinus Torvalds {
80424c1f931STakashi Iwai 	struct snd_info_entry *entry = snd_info_create_entry(name);
8051da177e4SLinus Torvalds 	if (entry) {
8061da177e4SLinus Torvalds 		entry->module = module;
8071da177e4SLinus Torvalds 		entry->parent = parent;
8081da177e4SLinus Torvalds 	}
8091da177e4SLinus Torvalds 	return entry;
8101da177e4SLinus Torvalds }
8111da177e4SLinus Torvalds 
812c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry);
813c0d3fb39STakashi Iwai 
8141da177e4SLinus Torvalds /**
8151da177e4SLinus Torvalds  * snd_info_create_card_entry - create an info entry for the given card
8161da177e4SLinus Torvalds  * @card: the card instance
8171da177e4SLinus Torvalds  * @name: the file name
8181da177e4SLinus Torvalds  * @parent: the parent directory
8191da177e4SLinus Torvalds  *
8201da177e4SLinus Torvalds  * Creates a new info entry and assigns it to the given card.
8211da177e4SLinus Torvalds  *
8221da177e4SLinus Torvalds  * Returns the pointer of the new instance, or NULL on failure.
8231da177e4SLinus Torvalds  */
82424c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
8251da177e4SLinus Torvalds 					     const char *name,
82624c1f931STakashi Iwai 					     struct snd_info_entry * parent)
8271da177e4SLinus Torvalds {
82824c1f931STakashi Iwai 	struct snd_info_entry *entry = snd_info_create_entry(name);
8291da177e4SLinus Torvalds 	if (entry) {
8301da177e4SLinus Torvalds 		entry->module = card->module;
8311da177e4SLinus Torvalds 		entry->card = card;
8321da177e4SLinus Torvalds 		entry->parent = parent;
8331da177e4SLinus Torvalds 	}
8341da177e4SLinus Torvalds 	return entry;
8351da177e4SLinus Torvalds }
8361da177e4SLinus Torvalds 
837c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry);
838c0d3fb39STakashi Iwai 
839746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry)
840746d4a02STakashi Iwai {
841746d4a02STakashi Iwai 	struct list_head *p, *n;
842746d4a02STakashi Iwai 	struct proc_dir_entry *root;
843746d4a02STakashi Iwai 
844746d4a02STakashi Iwai 	list_for_each_safe(p, n, &entry->children) {
845746d4a02STakashi Iwai 		snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
846746d4a02STakashi Iwai 	}
847746d4a02STakashi Iwai 
848746d4a02STakashi Iwai 	if (! entry->p)
849746d4a02STakashi Iwai 		return;
850746d4a02STakashi Iwai 	list_del_init(&entry->list);
851746d4a02STakashi Iwai 	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
852746d4a02STakashi Iwai 	snd_assert(root, return);
853746d4a02STakashi Iwai 	snd_remove_proc_entry(root, entry->p);
854746d4a02STakashi Iwai 	entry->p = NULL;
855746d4a02STakashi Iwai }
856746d4a02STakashi Iwai 
85724c1f931STakashi Iwai static int snd_info_dev_free_entry(struct snd_device *device)
8581da177e4SLinus Torvalds {
85924c1f931STakashi Iwai 	struct snd_info_entry *entry = device->device_data;
8601da177e4SLinus Torvalds 	snd_info_free_entry(entry);
8611da177e4SLinus Torvalds 	return 0;
8621da177e4SLinus Torvalds }
8631da177e4SLinus Torvalds 
86424c1f931STakashi Iwai static int snd_info_dev_register_entry(struct snd_device *device)
8651da177e4SLinus Torvalds {
86624c1f931STakashi Iwai 	struct snd_info_entry *entry = device->device_data;
8671da177e4SLinus Torvalds 	return snd_info_register(entry);
8681da177e4SLinus Torvalds }
8691da177e4SLinus Torvalds 
8701da177e4SLinus Torvalds /**
8711da177e4SLinus Torvalds  * snd_card_proc_new - create an info entry for the given card
8721da177e4SLinus Torvalds  * @card: the card instance
8731da177e4SLinus Torvalds  * @name: the file name
8741da177e4SLinus Torvalds  * @entryp: the pointer to store the new info entry
8751da177e4SLinus Torvalds  *
8761da177e4SLinus Torvalds  * Creates a new info entry and assigns it to the given card.
8771da177e4SLinus Torvalds  * Unlike snd_info_create_card_entry(), this function registers the
8781da177e4SLinus Torvalds  * info entry as an ALSA device component, so that it can be
8791da177e4SLinus Torvalds  * unregistered/released without explicit call.
8801da177e4SLinus Torvalds  * Also, you don't have to register this entry via snd_info_register(),
8811da177e4SLinus Torvalds  * since this will be registered by snd_card_register() automatically.
8821da177e4SLinus Torvalds  *
8831da177e4SLinus Torvalds  * The parent is assumed as card->proc_root.
8841da177e4SLinus Torvalds  *
8851da177e4SLinus Torvalds  * For releasing this entry, use snd_device_free() instead of
8861da177e4SLinus Torvalds  * snd_info_free_entry().
8871da177e4SLinus Torvalds  *
8881da177e4SLinus Torvalds  * Returns zero if successful, or a negative error code on failure.
8891da177e4SLinus Torvalds  */
89024c1f931STakashi Iwai int snd_card_proc_new(struct snd_card *card, const char *name,
89124c1f931STakashi Iwai 		      struct snd_info_entry **entryp)
8921da177e4SLinus Torvalds {
89324c1f931STakashi Iwai 	static struct snd_device_ops ops = {
8941da177e4SLinus Torvalds 		.dev_free = snd_info_dev_free_entry,
8951da177e4SLinus Torvalds 		.dev_register =	snd_info_dev_register_entry,
896746d4a02STakashi Iwai 		/* disconnect is done via snd_info_card_disconnect() */
8971da177e4SLinus Torvalds 	};
89824c1f931STakashi Iwai 	struct snd_info_entry *entry;
8991da177e4SLinus Torvalds 	int err;
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	entry = snd_info_create_card_entry(card, name, card->proc_root);
9021da177e4SLinus Torvalds 	if (! entry)
9031da177e4SLinus Torvalds 		return -ENOMEM;
9041da177e4SLinus Torvalds 	if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
9051da177e4SLinus Torvalds 		snd_info_free_entry(entry);
9061da177e4SLinus Torvalds 		return err;
9071da177e4SLinus Torvalds 	}
9081da177e4SLinus Torvalds 	if (entryp)
9091da177e4SLinus Torvalds 		*entryp = entry;
9101da177e4SLinus Torvalds 	return 0;
9111da177e4SLinus Torvalds }
9121da177e4SLinus Torvalds 
913c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_proc_new);
914c0d3fb39STakashi Iwai 
9151da177e4SLinus Torvalds /**
9161da177e4SLinus Torvalds  * snd_info_free_entry - release the info entry
9171da177e4SLinus Torvalds  * @entry: the info entry
9181da177e4SLinus Torvalds  *
9191da177e4SLinus Torvalds  * Releases the info entry.  Don't call this after registered.
9201da177e4SLinus Torvalds  */
92124c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry)
9221da177e4SLinus Torvalds {
9231da177e4SLinus Torvalds 	if (entry == NULL)
9241da177e4SLinus Torvalds 		return;
925746d4a02STakashi Iwai 	if (entry->p) {
926746d4a02STakashi Iwai 		mutex_lock(&info_mutex);
927746d4a02STakashi Iwai 		snd_info_disconnect(entry);
928746d4a02STakashi Iwai 		mutex_unlock(&info_mutex);
929746d4a02STakashi Iwai 	}
9301da177e4SLinus Torvalds 	kfree(entry->name);
9311da177e4SLinus Torvalds 	if (entry->private_free)
9321da177e4SLinus Torvalds 		entry->private_free(entry);
9331da177e4SLinus Torvalds 	kfree(entry);
9341da177e4SLinus Torvalds }
9351da177e4SLinus Torvalds 
936c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry);
937c0d3fb39STakashi Iwai 
9381da177e4SLinus Torvalds /**
9391da177e4SLinus Torvalds  * snd_info_register - register the info entry
9401da177e4SLinus Torvalds  * @entry: the info entry
9411da177e4SLinus Torvalds  *
9421da177e4SLinus Torvalds  * Registers the proc info entry.
9431da177e4SLinus Torvalds  *
9441da177e4SLinus Torvalds  * Returns zero if successful, or a negative error code on failure.
9451da177e4SLinus Torvalds  */
94624c1f931STakashi Iwai int snd_info_register(struct snd_info_entry * entry)
9471da177e4SLinus Torvalds {
9481da177e4SLinus Torvalds 	struct proc_dir_entry *root, *p = NULL;
9491da177e4SLinus Torvalds 
9501da177e4SLinus Torvalds 	snd_assert(entry != NULL, return -ENXIO);
9511da177e4SLinus Torvalds 	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
9521a60d4c5SIngo Molnar 	mutex_lock(&info_mutex);
9531da177e4SLinus Torvalds 	p = snd_create_proc_entry(entry->name, entry->mode, root);
9541da177e4SLinus Torvalds 	if (!p) {
9551a60d4c5SIngo Molnar 		mutex_unlock(&info_mutex);
9561da177e4SLinus Torvalds 		return -ENOMEM;
9571da177e4SLinus Torvalds 	}
9581da177e4SLinus Torvalds 	p->owner = entry->module;
9591da177e4SLinus Torvalds 	if (!S_ISDIR(entry->mode))
9601da177e4SLinus Torvalds 		p->proc_fops = &snd_info_entry_operations;
9611da177e4SLinus Torvalds 	p->size = entry->size;
9621da177e4SLinus Torvalds 	p->data = entry;
9631da177e4SLinus Torvalds 	entry->p = p;
964746d4a02STakashi Iwai 	if (entry->parent)
965746d4a02STakashi Iwai 		list_add_tail(&entry->list, &entry->parent->children);
9661a60d4c5SIngo Molnar 	mutex_unlock(&info_mutex);
9671da177e4SLinus Torvalds 	return 0;
9681da177e4SLinus Torvalds }
9691da177e4SLinus Torvalds 
970c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register);
971c0d3fb39STakashi Iwai 
9721da177e4SLinus Torvalds /*
9731da177e4SLinus Torvalds 
9741da177e4SLinus Torvalds  */
9751da177e4SLinus Torvalds 
9766581f4e7STakashi Iwai static struct snd_info_entry *snd_info_version_entry;
9771da177e4SLinus Torvalds 
97824c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
9791da177e4SLinus Torvalds {
9801da177e4SLinus Torvalds 	snd_iprintf(buffer,
9811da177e4SLinus Torvalds 		    "Advanced Linux Sound Architecture Driver Version "
9821da177e4SLinus Torvalds 		    CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"
9831da177e4SLinus Torvalds 		   );
9841da177e4SLinus Torvalds }
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds static int __init snd_info_version_init(void)
9871da177e4SLinus Torvalds {
98824c1f931STakashi Iwai 	struct snd_info_entry *entry;
9891da177e4SLinus Torvalds 
9901da177e4SLinus Torvalds 	entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
9911da177e4SLinus Torvalds 	if (entry == NULL)
9921da177e4SLinus Torvalds 		return -ENOMEM;
9931da177e4SLinus Torvalds 	entry->c.text.read = snd_info_version_read;
9941da177e4SLinus Torvalds 	if (snd_info_register(entry) < 0) {
9951da177e4SLinus Torvalds 		snd_info_free_entry(entry);
9961da177e4SLinus Torvalds 		return -ENOMEM;
9971da177e4SLinus Torvalds 	}
9981da177e4SLinus Torvalds 	snd_info_version_entry = entry;
9991da177e4SLinus Torvalds 	return 0;
10001da177e4SLinus Torvalds }
10011da177e4SLinus Torvalds 
10021da177e4SLinus Torvalds static int __exit snd_info_version_done(void)
10031da177e4SLinus Torvalds {
1004746d4a02STakashi Iwai 	snd_info_free_entry(snd_info_version_entry);
10051da177e4SLinus Torvalds 	return 0;
10061da177e4SLinus Torvalds }
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */
1009