// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Information interface for ALSA driver
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <linux/init.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
#include <linux/utsname.h>
#include <linux/proc_fs.h>
#include <linux/mutex.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;
}
static DEFINE_MUTEX(info_mutex);
struct snd_info_private_data {
struct snd_info_buffer *rbuffer;
struct snd_info_buffer *wbuffer;
struct snd_info_entry *entry;
void *file_private_data;
};
static int snd_info_version_init(void);
static void snd_info_disconnect(struct snd_info_entry *entry);
/*
*/
static struct snd_info_entry *snd_proc_root;
struct snd_info_entry *snd_seq_root;
EXPORT_SYMBOL(snd_seq_root);
#ifdef CONFIG_SND_OSSEMUL
struct snd_info_entry *snd_oss_root;
#endif
static int alloc_info_private(struct snd_info_entry *entry,
struct snd_info_private_data **ret)
{
struct snd_info_private_data *data;
if (!entry || !entry->p)
return -ENODEV;
if (!try_module_get(entry->module))
return -EFAULT;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
module_put(entry->module);
return -ENOMEM;
}
data->entry = entry;
*ret = data;
return 0;
}
static bool valid_pos(loff_t pos, size_t count)
{
if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
return false;
if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
return false;
return true;
}
/*
* file ops for binary proc files
*/
static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
{
struct snd_info_private_data *data;
struct snd_info_entry *entry;
loff_t ret = -EINVAL, size;
data = file->private_data;
entry = data->entry;
mutex_lock(&entry->access);
if (entry->c.ops->llseek) {
offset = entry->c.ops->llseek(entry,
data->file_private_data,
file, offset, orig);
goto out;
}
size = entry->size;
switch (orig) {
case SEEK_SET:
break;
case SEEK_CUR:
offset += file->f_pos;
break;
case SEEK_END:
if (!size)
goto out;
offset += size;
break;
default:
goto out;
}
if (offset < 0)
goto out;
if (size && offset > size)
offset = size;
file->f_pos = offset;
ret = offset;
out:
mutex_unlock(&entry->access);
return ret;
}
static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
size_t count, loff_t * offset)
{
struct snd_info_private_data *data = file->private_data;
struct snd_info_entry *entry = data->entry;
size_t size;
loff_t pos;
pos = *offset;
if (!valid_pos(pos, count))
return -EIO;
if (pos >= entry->size)
return 0;
size = entry->size - pos;
size = min(count, size);
size = entry->c.ops->read(entry, data->file_private_data,
file, buffer, size, pos);
if ((ssize_t) size > 0)
*offset = pos + size;
return size;
}
static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
size_t count, loff_t * offset)
{
struct snd_info_private_data *data = file->private_data;
struct snd_info_entry *entry = data->entry;
ssize_t size = 0;
loff_t pos;
pos = *offset;
if (!valid_pos(pos, count))
return -EIO;
if (count > 0) {
size_t maxsize = entry->size - pos;
count = min(count, maxsize);
size = entry->c.ops->write(entry, data->file_private_data,
file, buffer, count, pos);
}
if (size > 0)
*offset = pos + size;
return size;
}
static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
{
struct snd_info_private_data *data = file->private_data;
struct snd_info_entry *entry = data->entry;
__poll_t mask = 0;
if (entry->c.ops->poll)
return entry->c.ops->poll(entry,
data->file_private_data,
file, wait);
if (entry->c.ops->read)
mask |= EPOLLIN | EPOLLRDNORM;
if (entry->c.ops->write)
mask |= EPOLLOUT | EPOLLWRNORM;
return mask;
}
static long snd_info_entry_ioctl(