/*
* Information interface for ALSA driver
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/time.h>
#include <linux/smp_lock.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
#include <sound/version.h>
#include <linux/proc_fs.h>
#include <linux/devfs_fs_kernel.h>
#include <stdarg.h>
/*
*
*/
int snd_info_check_reserved_words(const char *str)
{
static char *reserved[] =
{
"version",
"meminfo",
"memdebug",
"detect",
"devices",
"oss",
"cards",
"timers",
"synth",
"pcm",
"seq",
NULL
};
char **xstr = reserved;
while (*xstr) {
if (!strcmp(*xstr, str))
return 0;
xstr++;
}
if (!strncmp(str, "card", 4))
return 0;
return 1;
}
#ifdef CONFIG_PROC_FS
static DECLARE_MUTEX(info_mutex);
typedef struct _snd_info_private_data {
snd_info_buffer_t *rbuffer;
snd_info_buffer_t *wbuffer;
snd_info_entry_t *entry;
void *file_private_data;
} snd_info_private_data_t;
static int snd_info_version_init(void);
static int snd_info_version_done(void);
/**
* snd_iprintf - printf on the procfs buffer
* @buffer: the procfs buffer
* @fmt: the printf format
*
* Outputs the string on the procfs buffer just like printf().
*
* Returns the size of output string.
*/
int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...)
{
va_list args;
int len, res;
if (buffer->stop || buffer->error)
return 0;
len = buffer->len - buffer->size;
va_start(args, fmt);
res = vsnprintf(buffer->curr, len, fmt, args);
va_end(args);
if (res >= len) {
buffer->stop = 1;
return 0;
}
buffer->curr += res;
buffer->size += res;
return res;
}
/*
*/
static struct proc_dir_entry *snd_proc_root = NULL;
snd_info_entry_t *snd_seq_root = NULL;
#ifdef CONFIG_SND_OSSEMUL
snd_info_entry_t *snd_oss_root = NULL;
#endif
static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
{
de->owner = THIS_MODULE;
}
static void snd_remove_proc_entry(struct proc_dir_entry *parent,
struct proc_dir_entry *de)
{
if (de)
remove_proc_entry(de->name, parent);
}
static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
{
snd_info_private_data_t *data;
struct snd_info_entry *entry;
loff_t ret;
data = file->private_data;
entry = data->entry;
lock_kernel();
switch (entry->content) {
case SNDRV_INFO_CONTENT_TEXT:
switch (orig) {
case 0: /* SEEK_SET */
file->f_pos = offset;
ret = file->f_pos;
goto out;
case 1: /* SEEK_CUR */
file->f_pos += offset;
ret = file->f_pos;
goto out;
case 2: /* SEEK_END */
default:
ret = -EINVAL;
goto out;
}
break;
case SNDRV_INFO_CONTENT_DATA:
if (entry->c.ops->llseek) {
ret = entry->c.ops->llseek(entry,
data->file_private_data,
file, offset, orig);
goto out;
}
break;
}
ret = -ENXIO;
out:
unlock_kernel();
return ret;
}
static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
size_t count, loff_t * offset)
{
snd_info_private_data_t *data;
struct snd_info_entry *entry;
snd_info_buffer_t *buf;
size_t size = 0;
loff_t pos;
data = file->private_data;
snd_assert(data