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 + -
显示快捷键?