info.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,001 行 · 第 1/2 页

C
1,001
字号
/* *  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/version.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_FSstatic 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 res;	char sbuffer[512];	if (buffer->stop || buffer->error)		return 0;	va_start(args, fmt);	res = vscnprintf(sbuffer, sizeof(sbuffer), fmt, args);	va_end(args);	if (buffer->size + res >= buffer->len) {		buffer->stop = 1;		return 0;	}	strcpy(buffer->curr, sbuffer);	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_OSSEMULsnd_info_entry_t *snd_oss_root = NULL;#endifstatic inline void snd_info_entry_prepare(struct proc_dir_entry *de){	de->owner = THIS_MODULE;}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 != NULL, return -ENXIO);	pos = *offset;	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)		return -EIO;	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)		return -EIO;	entry = data->entry;	switch (entry->content) {	case SNDRV_INFO_CONTENT_TEXT:		buf = data->rbuffer;		if (buf == NULL)			return -EIO;		if (pos >= buf->size)			return 0;		size = buf->size - pos;		size = min(count, size);		if (copy_to_user(buffer, buf->buffer + pos, size))			return -EFAULT;		break;	case SNDRV_INFO_CONTENT_DATA:		if (entry->c.ops->read)			size = entry->c.ops->read(entry,						  data->file_private_data,						  file, buffer, count, pos);		break;	}	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){	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 != NULL, return -ENXIO);	entry = data->entry;	pos = *offset;	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)		return -EIO;	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)		return -EIO;	switch (entry->content) {	case SNDRV_INFO_CONTENT_TEXT:		buf = data->wbuffer;		if (buf == NULL)			return -EIO;		if (pos >= buf->len)			return -ENOMEM;		size = buf->len - pos;		size = min(count, size);		if (copy_from_user(buf->buffer + pos, buffer, size))			return -EFAULT;		if ((long)buf->size < pos + size)			buf->size = pos + size;		break;	case SNDRV_INFO_CONTENT_DATA:		if (entry->c.ops->write)			size = entry->c.ops->write(entry,						   data->file_private_data,						   file, buffer, count, pos);		break;	}	if ((ssize_t) size > 0)		*offset = pos + size;	return size;}static int snd_info_entry_open(struct inode *inode, struct file *file){	snd_info_entry_t *entry;	snd_info_private_data_t *data;	snd_info_buffer_t *buffer;	struct proc_dir_entry *p;	int mode, err;	down(&info_mutex);	p = PDE(inode);	entry = p == NULL ? NULL : (snd_info_entry_t *)p->data;	if (entry == NULL || entry->disconnected) {		up(&info_mutex);		return -ENODEV;	}	if (!try_module_get(entry->module)) {		err = -EFAULT;		goto __error1;	}	mode = file->f_flags & O_ACCMODE;	if (mode == O_RDONLY || mode == O_RDWR) {		if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&		     !entry->c.text.read_size) ||		    (entry->content == SNDRV_INFO_CONTENT_DATA &&		     entry->c.ops->read == NULL)) {		    	err = -ENODEV;		    	goto __error;		}	}	if (mode == O_WRONLY || mode == O_RDWR) {		if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&		     !entry->c.text.write_size) ||		    (entry->content == SNDRV_INFO_CONTENT_DATA &&		     entry->c.ops->write == NULL)) {		    	err = -ENODEV;		    	goto __error;		}	}	data = kcalloc(1, sizeof(*data), GFP_KERNEL);	if (data == NULL) {		err = -ENOMEM;		goto __error;	}	data->entry = entry;	switch (entry->content) {	case SNDRV_INFO_CONTENT_TEXT:		if (mode == O_RDONLY || mode == O_RDWR) {			buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL);			if (buffer == NULL) {				kfree(data);				err = -ENOMEM;				goto __error;			}			buffer->len = (entry->c.text.read_size +				      (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);			buffer->buffer = vmalloc(buffer->len);			if (buffer->buffer == NULL) {				kfree(buffer);				kfree(data);				err = -ENOMEM;				goto __error;			}			buffer->curr = buffer->buffer;			data->rbuffer = buffer;		}		if (mode == O_WRONLY || mode == O_RDWR) {			buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL);			if (buffer == NULL) {				if (mode == O_RDWR) {					vfree(data->rbuffer->buffer);					kfree(data->rbuffer);				}				kfree(data);				err = -ENOMEM;				goto __error;			}			buffer->len = (entry->c.text.write_size +				      (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);			buffer->buffer = vmalloc(buffer->len);			if (buffer->buffer == NULL) {				if (mode == O_RDWR) {					vfree(data->rbuffer->buffer);					kfree(data->rbuffer);				}				kfree(buffer);				kfree(data);				err = -ENOMEM;				goto __error;			}			buffer->curr = buffer->buffer;			data->wbuffer = buffer;		}		break;	case SNDRV_INFO_CONTENT_DATA:	/* data */		if (entry->c.ops->open) {			if ((err = entry->c.ops->open(entry, mode,						      &data->file_private_data)) < 0) {				kfree(data);				goto __error;			}		}		break;	}	file->private_data = data;	up(&info_mutex);	if (entry->content == SNDRV_INFO_CONTENT_TEXT &&	    (mode == O_RDONLY || mode == O_RDWR)) {		if (entry->c.text.read) {			down(&entry->access);			entry->c.text.read(entry, data->rbuffer);			up(&entry->access);		}	}	return 0;      __error:	module_put(entry->module);      __error1:	up(&info_mutex);	return err;}static int snd_info_entry_release(struct inode *inode, struct file *file){	snd_info_entry_t *entry;	snd_info_private_data_t *data;	int mode;	mode = file->f_flags & O_ACCMODE;	data = file->private_data;	entry = data->entry;	switch (entry->content) {	case SNDRV_INFO_CONTENT_TEXT:		if (mode == O_RDONLY || mode == O_RDWR) {			vfree(data->rbuffer->buffer);			kfree(data->rbuffer);		}		if (mode == O_WRONLY || mode == O_RDWR) {			if (entry->c.text.write) {				entry->c.text.write(entry, data->wbuffer);				if (data->wbuffer->error) {					snd_printk(KERN_WARNING "data write error to %s (%i)\n",						entry->name,						data->wbuffer->error);				}			}			vfree(data->wbuffer->buffer);			kfree(data->wbuffer);		}		break;	case SNDRV_INFO_CONTENT_DATA:		if (entry->c.ops->release)			entry->c.ops->release(entry, mode,					      data->file_private_data);		break;	}	module_put(entry->module);	kfree(data);	return 0;}static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait){	snd_info_private_data_t *data;	struct snd_info_entry *entry;	unsigned int mask;	data = file->private_data;	if (data == NULL)		return 0;	entry = data->entry;	mask = 0;	switch (entry->content) {	case SNDRV_INFO_CONTENT_DATA:		if (entry->c.ops->poll)			return entry->c.ops->poll(entry,						  data->file_private_data,						  file, wait);		if (entry->c.ops->read)			mask |= POLLIN | POLLRDNORM;		if (entry->c.ops->write)			mask |= POLLOUT | POLLWRNORM;		break;	}	return mask;}static inline int _snd_info_entry_ioctl(struct inode *inode, struct file *file,					unsigned int cmd, unsigned long arg){	snd_info_private_data_t *data;	struct snd_info_entry *entry;	data = file->private_data;	if (data == NULL)		return 0;	entry = data->entry;	switch (entry->content) {	case SNDRV_INFO_CONTENT_DATA:		if (entry->c.ops->ioctl)			return entry->c.ops->ioctl(entry,						   data->file_private_data,						   file, cmd, arg);		break;	}	return -ENOTTY;}/* FIXME: need to unlock BKL to allow preemption */static int snd_info_entry_ioctl(struct inode *inode, struct file *file,				unsigned int cmd, unsigned long arg){	int err;	unlock_kernel();	err = _snd_info_entry_ioctl(inode, file, cmd, arg);	lock_kernel();	return err;}static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma){	struct inode *inode = file->f_dentry->d_inode;	snd_info_private_data_t *data;	struct snd_info_entry *entry;	data = file->private_data;	if (data == NULL)		return 0;	entry = data->entry;	switch (entry->content) {	case SNDRV_INFO_CONTENT_DATA:		if (entry->c.ops->mmap)			return entry->c.ops->mmap(entry,						  data->file_private_data,						  inode, file, vma);		break;	}	return -ENXIO;}static struct file_operations snd_info_entry_operations =

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?