📄 info.c
字号:
/* * Information interface for ALSA driver * Copyright (c) by Jaroslav Kysela <perex@perex.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/time.h>#include <linux/smp_lock.h>#include <linux/string.h>#include <sound/core.h>#include <sound/minors.h>#include <sound/info.h>#include <sound/version.h>#include <linux/proc_fs.h>#include <linux/mutex.h>#include <stdarg.h>/* * */#ifdef CONFIG_PROC_FSint 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 int snd_info_version_done(void);static void snd_info_disconnect(struct snd_info_entry *entry);/* resize the proc r/w buffer */static int resize_info_buffer(struct snd_info_buffer *buffer, unsigned int nsize){ char *nbuf; nsize = PAGE_ALIGN(nsize); nbuf = kmalloc(nsize, GFP_KERNEL); if (! nbuf) return -ENOMEM; memcpy(nbuf, buffer->buffer, buffer->len); kfree(buffer->buffer); buffer->buffer = nbuf; buffer->len = nsize; return 0;}/** * 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(struct snd_info_buffer *buffer, char *fmt,...){ va_list args; int len, res; int err = 0; might_sleep(); if (buffer->stop || buffer->error) return 0; len = buffer->len - buffer->size; va_start(args, fmt); for (;;) { va_list ap; va_copy(ap, args); res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap); va_end(ap); if (res < len) break; err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE); if (err < 0) break; len = buffer->len - buffer->size; } va_end(args); if (err < 0) return err; buffer->curr += res; buffer->size += res; return res;}EXPORT_SYMBOL(snd_iprintf);/* */static struct proc_dir_entry *snd_proc_root;struct snd_info_entry *snd_seq_root;EXPORT_SYMBOL(snd_seq_root);#ifdef CONFIG_SND_OSSEMULstruct snd_info_entry *snd_oss_root;#endifstatic 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){ struct snd_info_private_data *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 SEEK_SET: file->f_pos = offset; ret = file->f_pos; goto out; case SEEK_CUR: file->f_pos += offset; ret = file->f_pos; goto out; case 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){ struct snd_info_private_data *data; struct snd_info_entry *entry; struct snd_info_buffer *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){ struct snd_info_private_data *data; struct snd_info_entry *entry; struct snd_info_buffer *buf; ssize_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; mutex_lock(&entry->access); if (pos + count >= buf->len) { if (resize_info_buffer(buf, pos + count)) { mutex_unlock(&entry->access); return -ENOMEM; } } if (copy_from_user(buf->buffer + pos, buffer, count)) { mutex_unlock(&entry->access); return -EFAULT; } buf->size = pos + count; mutex_unlock(&entry->access); size = count; 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){ struct snd_info_entry *entry; struct snd_info_private_data *data; struct snd_info_buffer *buffer; struct proc_dir_entry *p; int mode, err; mutex_lock(&info_mutex); p = PDE(inode); entry = p == NULL ? NULL : (struct snd_info_entry *)p->data; if (entry == NULL || ! entry->p) { mutex_unlock(&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_DATA && entry->c.ops->read == NULL)) { err = -ENODEV; goto __error; } } if (mode == O_WRONLY || mode == O_RDWR) { if ((entry->content == SNDRV_INFO_CONTENT_DATA && entry->c.ops->write == NULL)) { err = -ENODEV; goto __error; } } data = kzalloc(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 = kzalloc(sizeof(*buffer), GFP_KERNEL); if (buffer == NULL) goto __nomem; data->rbuffer = buffer; buffer->len = PAGE_SIZE; buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); if (buffer->buffer == NULL) goto __nomem; } if (mode == O_WRONLY || mode == O_RDWR) { buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); if (buffer == NULL) goto __nomem; data->wbuffer = buffer; buffer->len = PAGE_SIZE; buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); if (buffer->buffer == NULL) goto __nomem; } 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; mutex_unlock(&info_mutex); if (entry->content == SNDRV_INFO_CONTENT_TEXT && (mode == O_RDONLY || mode == O_RDWR)) { if (entry->c.text.read) { mutex_lock(&entry->access); entry->c.text.read(entry, data->rbuffer); mutex_unlock(&entry->access); } } return 0; __nomem: if (data->rbuffer) { kfree(data->rbuffer->buffer); kfree(data->rbuffer); } if (data->wbuffer) { kfree(data->wbuffer->buffer); kfree(data->wbuffer); } kfree(data); err = -ENOMEM; __error: module_put(entry->module); __error1: mutex_unlock(&info_mutex); return err;}static int snd_info_entry_release(struct inode *inode, struct file *file){ struct snd_info_entry *entry; struct snd_info_private_data *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 (data->rbuffer) { kfree(data->rbuffer->buffer); kfree(data->rbuffer); } if (data->wbuffer) { 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); } } kfree(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){ struct snd_info_private_data *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 long snd_info_entry_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ struct snd_info_private_data *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;}static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma){ struct inode *inode = file->f_path.dentry->d_inode; struct snd_info_private_data *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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -