11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Information interface for ALSA driver 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 91da177e4SLinus Torvalds * (at your option) any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 171da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/init.h> 231da177e4SLinus Torvalds #include <linux/time.h> 2427ac792cSAndrea Righi #include <linux/mm.h> 255a0e3ad6STejun Heo #include <linux/slab.h> 26543537bdSPaulo Marques #include <linux/string.h> 27da155d5bSPaul Gortmaker #include <linux/module.h> 281da177e4SLinus Torvalds #include <sound/core.h> 291da177e4SLinus Torvalds #include <sound/minors.h> 301da177e4SLinus Torvalds #include <sound/info.h> 3142662748SJaroslav Kysela #include <linux/utsname.h> 321da177e4SLinus Torvalds #include <linux/proc_fs.h> 331a60d4c5SIngo Molnar #include <linux/mutex.h> 341da177e4SLinus Torvalds #include <stdarg.h> 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds /* 371da177e4SLinus Torvalds * 381da177e4SLinus Torvalds */ 391da177e4SLinus Torvalds 40e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 41e28563ccSTakashi Iwai 421da177e4SLinus Torvalds int snd_info_check_reserved_words(const char *str) 431da177e4SLinus Torvalds { 441da177e4SLinus Torvalds static char *reserved[] = 451da177e4SLinus Torvalds { 461da177e4SLinus Torvalds "version", 471da177e4SLinus Torvalds "meminfo", 481da177e4SLinus Torvalds "memdebug", 491da177e4SLinus Torvalds "detect", 501da177e4SLinus Torvalds "devices", 511da177e4SLinus Torvalds "oss", 521da177e4SLinus Torvalds "cards", 531da177e4SLinus Torvalds "timers", 541da177e4SLinus Torvalds "synth", 551da177e4SLinus Torvalds "pcm", 561da177e4SLinus Torvalds "seq", 571da177e4SLinus Torvalds NULL 581da177e4SLinus Torvalds }; 591da177e4SLinus Torvalds char **xstr = reserved; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds while (*xstr) { 621da177e4SLinus Torvalds if (!strcmp(*xstr, str)) 631da177e4SLinus Torvalds return 0; 641da177e4SLinus Torvalds xstr++; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds if (!strncmp(str, "card", 4)) 671da177e4SLinus Torvalds return 0; 681da177e4SLinus Torvalds return 1; 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex); 721da177e4SLinus Torvalds 7324c1f931STakashi Iwai struct snd_info_private_data { 7424c1f931STakashi Iwai struct snd_info_buffer *rbuffer; 7524c1f931STakashi Iwai struct snd_info_buffer *wbuffer; 7624c1f931STakashi Iwai struct snd_info_entry *entry; 771da177e4SLinus Torvalds void *file_private_data; 7824c1f931STakashi Iwai }; 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds static int snd_info_version_init(void); 811da177e4SLinus Torvalds static int snd_info_version_done(void); 82746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry); 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds /* 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds */ 871da177e4SLinus Torvalds 886581f4e7STakashi Iwai static struct proc_dir_entry *snd_proc_root; 896581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root; 90c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root); 91c0d3fb39STakashi Iwai 921da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 936581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root; 941da177e4SLinus Torvalds #endif 951da177e4SLinus Torvalds 964adb7bcbSTakashi Iwai static int alloc_info_private(struct snd_info_entry *entry, 974adb7bcbSTakashi Iwai struct snd_info_private_data **ret) 984adb7bcbSTakashi Iwai { 994adb7bcbSTakashi Iwai struct snd_info_private_data *data; 1004adb7bcbSTakashi Iwai 1014adb7bcbSTakashi Iwai if (!entry || !entry->p) 1024adb7bcbSTakashi Iwai return -ENODEV; 1034adb7bcbSTakashi Iwai if (!try_module_get(entry->module)) 1044adb7bcbSTakashi Iwai return -EFAULT; 1054adb7bcbSTakashi Iwai data = kzalloc(sizeof(*data), GFP_KERNEL); 1064adb7bcbSTakashi Iwai if (!data) { 1074adb7bcbSTakashi Iwai module_put(entry->module); 1084adb7bcbSTakashi Iwai return -ENOMEM; 1094adb7bcbSTakashi Iwai } 1104adb7bcbSTakashi Iwai data->entry = entry; 1114adb7bcbSTakashi Iwai *ret = data; 1124adb7bcbSTakashi Iwai return 0; 1134adb7bcbSTakashi Iwai } 1144adb7bcbSTakashi Iwai 1154adb7bcbSTakashi Iwai static bool valid_pos(loff_t pos, size_t count) 1164adb7bcbSTakashi Iwai { 1174adb7bcbSTakashi Iwai if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 1184adb7bcbSTakashi Iwai return false; 1194adb7bcbSTakashi Iwai if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 1204adb7bcbSTakashi Iwai return false; 1214adb7bcbSTakashi Iwai return true; 1224adb7bcbSTakashi Iwai } 1234adb7bcbSTakashi Iwai 1244adb7bcbSTakashi Iwai /* 1254adb7bcbSTakashi Iwai * file ops for binary proc files 1264adb7bcbSTakashi Iwai */ 1271da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) 1281da177e4SLinus Torvalds { 12924c1f931STakashi Iwai struct snd_info_private_data *data; 1301da177e4SLinus Torvalds struct snd_info_entry *entry; 13173029e0fSTakashi Iwai loff_t ret = -EINVAL, size; 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds data = file->private_data; 1341da177e4SLinus Torvalds entry = data->entry; 1355b5cd553STakashi Iwai mutex_lock(&entry->access); 1364adb7bcbSTakashi Iwai if (entry->c.ops->llseek) { 13773029e0fSTakashi Iwai offset = entry->c.ops->llseek(entry, 1381da177e4SLinus Torvalds data->file_private_data, 1391da177e4SLinus Torvalds file, offset, orig); 1401da177e4SLinus Torvalds goto out; 1411da177e4SLinus Torvalds } 1424adb7bcbSTakashi Iwai 14373029e0fSTakashi Iwai size = entry->size; 14473029e0fSTakashi Iwai switch (orig) { 14573029e0fSTakashi Iwai case SEEK_SET: 1461da177e4SLinus Torvalds break; 14773029e0fSTakashi Iwai case SEEK_CUR: 14873029e0fSTakashi Iwai offset += file->f_pos; 14973029e0fSTakashi Iwai break; 15073029e0fSTakashi Iwai case SEEK_END: 15173029e0fSTakashi Iwai if (!size) 15273029e0fSTakashi Iwai goto out; 15373029e0fSTakashi Iwai offset += size; 15473029e0fSTakashi Iwai break; 15573029e0fSTakashi Iwai default: 15673029e0fSTakashi Iwai goto out; 1571da177e4SLinus Torvalds } 15873029e0fSTakashi Iwai if (offset < 0) 15973029e0fSTakashi Iwai goto out; 16073029e0fSTakashi Iwai if (size && offset > size) 16173029e0fSTakashi Iwai offset = size; 16273029e0fSTakashi Iwai file->f_pos = offset; 16373029e0fSTakashi Iwai ret = offset; 1641da177e4SLinus Torvalds out: 1655b5cd553STakashi Iwai mutex_unlock(&entry->access); 1661da177e4SLinus Torvalds return ret; 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, 1701da177e4SLinus Torvalds size_t count, loff_t * offset) 1711da177e4SLinus Torvalds { 1724adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 1734adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 1744adb7bcbSTakashi Iwai size_t size; 1751da177e4SLinus Torvalds loff_t pos; 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds pos = *offset; 1784adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 1791da177e4SLinus Torvalds return -EIO; 180d97e1b78STakashi Iwai if (pos >= entry->size) 181d97e1b78STakashi Iwai return 0; 182d97e1b78STakashi Iwai size = entry->size - pos; 183d97e1b78STakashi Iwai size = min(count, size); 1844adb7bcbSTakashi Iwai size = entry->c.ops->read(entry, data->file_private_data, 185d97e1b78STakashi Iwai file, buffer, size, pos); 1861da177e4SLinus Torvalds if ((ssize_t) size > 0) 1871da177e4SLinus Torvalds *offset = pos + size; 1881da177e4SLinus Torvalds return size; 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, 1921da177e4SLinus Torvalds size_t count, loff_t * offset) 1931da177e4SLinus Torvalds { 1944adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 1954adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 1967e4eeec8STakashi Iwai ssize_t size = 0; 1971da177e4SLinus Torvalds loff_t pos; 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds pos = *offset; 2004adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 2011da177e4SLinus Torvalds return -EIO; 2024adb7bcbSTakashi Iwai if (count > 0) { 203d97e1b78STakashi Iwai size_t maxsize = entry->size - pos; 204d97e1b78STakashi Iwai count = min(count, maxsize); 2054adb7bcbSTakashi Iwai size = entry->c.ops->write(entry, data->file_private_data, 2061da177e4SLinus Torvalds file, buffer, count, pos); 207d97e1b78STakashi Iwai } 2084adb7bcbSTakashi Iwai if (size > 0) 2091da177e4SLinus Torvalds *offset = pos + size; 2101da177e4SLinus Torvalds return size; 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds static unsigned int snd_info_entry_poll(struct file *file, poll_table *wait) 2141da177e4SLinus Torvalds { 2154adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2164adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 2174adb7bcbSTakashi Iwai unsigned int mask = 0; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds if (entry->c.ops->poll) 2201da177e4SLinus Torvalds return entry->c.ops->poll(entry, 2211da177e4SLinus Torvalds data->file_private_data, 2221da177e4SLinus Torvalds file, wait); 2231da177e4SLinus Torvalds if (entry->c.ops->read) 2241da177e4SLinus Torvalds mask |= POLLIN | POLLRDNORM; 2251da177e4SLinus Torvalds if (entry->c.ops->write) 2261da177e4SLinus Torvalds mask |= POLLOUT | POLLWRNORM; 2271da177e4SLinus Torvalds return mask; 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds 230d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, 231d99e9889SIngo Molnar unsigned long arg) 2321da177e4SLinus Torvalds { 2334adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2344adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 2351da177e4SLinus Torvalds 2364adb7bcbSTakashi Iwai if (!entry->c.ops->ioctl) 2371da177e4SLinus Torvalds return -ENOTTY; 2384adb7bcbSTakashi Iwai return entry->c.ops->ioctl(entry, data->file_private_data, 2394adb7bcbSTakashi Iwai file, cmd, arg); 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) 2431da177e4SLinus Torvalds { 244496ad9aaSAl Viro struct inode *inode = file_inode(file); 24524c1f931STakashi Iwai struct snd_info_private_data *data; 2461da177e4SLinus Torvalds struct snd_info_entry *entry; 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds data = file->private_data; 2491da177e4SLinus Torvalds if (data == NULL) 2501da177e4SLinus Torvalds return 0; 2511da177e4SLinus Torvalds entry = data->entry; 2524adb7bcbSTakashi Iwai if (!entry->c.ops->mmap) 2531da177e4SLinus Torvalds return -ENXIO; 2544adb7bcbSTakashi Iwai return entry->c.ops->mmap(entry, data->file_private_data, 2554adb7bcbSTakashi Iwai inode, file, vma); 2564adb7bcbSTakashi Iwai } 2574adb7bcbSTakashi Iwai 2584adb7bcbSTakashi Iwai static int snd_info_entry_open(struct inode *inode, struct file *file) 2594adb7bcbSTakashi Iwai { 2604adb7bcbSTakashi Iwai struct snd_info_entry *entry = PDE_DATA(inode); 2614adb7bcbSTakashi Iwai struct snd_info_private_data *data; 2624adb7bcbSTakashi Iwai int mode, err; 2634adb7bcbSTakashi Iwai 2644adb7bcbSTakashi Iwai mutex_lock(&info_mutex); 2654adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data); 2664adb7bcbSTakashi Iwai if (err < 0) 2674adb7bcbSTakashi Iwai goto unlock; 2684adb7bcbSTakashi Iwai 2694adb7bcbSTakashi Iwai mode = file->f_flags & O_ACCMODE; 2704adb7bcbSTakashi Iwai if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) || 2714adb7bcbSTakashi Iwai ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) { 2724adb7bcbSTakashi Iwai err = -ENODEV; 2734adb7bcbSTakashi Iwai goto error; 2744adb7bcbSTakashi Iwai } 2754adb7bcbSTakashi Iwai 2764adb7bcbSTakashi Iwai if (entry->c.ops->open) { 2774adb7bcbSTakashi Iwai err = entry->c.ops->open(entry, mode, &data->file_private_data); 2784adb7bcbSTakashi Iwai if (err < 0) 2794adb7bcbSTakashi Iwai goto error; 2804adb7bcbSTakashi Iwai } 2814adb7bcbSTakashi Iwai 2824adb7bcbSTakashi Iwai file->private_data = data; 2834adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 2844adb7bcbSTakashi Iwai return 0; 2854adb7bcbSTakashi Iwai 2864adb7bcbSTakashi Iwai error: 2874adb7bcbSTakashi Iwai kfree(data); 2884adb7bcbSTakashi Iwai module_put(entry->module); 2894adb7bcbSTakashi Iwai unlock: 2904adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 2914adb7bcbSTakashi Iwai return err; 2924adb7bcbSTakashi Iwai } 2934adb7bcbSTakashi Iwai 2944adb7bcbSTakashi Iwai static int snd_info_entry_release(struct inode *inode, struct file *file) 2954adb7bcbSTakashi Iwai { 2964adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2974adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 2984adb7bcbSTakashi Iwai 2994adb7bcbSTakashi Iwai if (entry->c.ops->release) 3004adb7bcbSTakashi Iwai entry->c.ops->release(entry, file->f_flags & O_ACCMODE, 3014adb7bcbSTakashi Iwai data->file_private_data); 3024adb7bcbSTakashi Iwai module_put(entry->module); 3034adb7bcbSTakashi Iwai kfree(data); 3044adb7bcbSTakashi Iwai return 0; 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds 3079c2e08c5SArjan van de Ven static const struct file_operations snd_info_entry_operations = 3081da177e4SLinus Torvalds { 3091da177e4SLinus Torvalds .owner = THIS_MODULE, 3101da177e4SLinus Torvalds .llseek = snd_info_entry_llseek, 3111da177e4SLinus Torvalds .read = snd_info_entry_read, 3121da177e4SLinus Torvalds .write = snd_info_entry_write, 3131da177e4SLinus Torvalds .poll = snd_info_entry_poll, 314d99e9889SIngo Molnar .unlocked_ioctl = snd_info_entry_ioctl, 3151da177e4SLinus Torvalds .mmap = snd_info_entry_mmap, 3161da177e4SLinus Torvalds .open = snd_info_entry_open, 3171da177e4SLinus Torvalds .release = snd_info_entry_release, 3181da177e4SLinus Torvalds }; 3191da177e4SLinus Torvalds 3204adb7bcbSTakashi Iwai /* 3214adb7bcbSTakashi Iwai * file ops for text proc files 3224adb7bcbSTakashi Iwai */ 3234adb7bcbSTakashi Iwai static ssize_t snd_info_text_entry_write(struct file *file, 3244adb7bcbSTakashi Iwai const char __user *buffer, 3254adb7bcbSTakashi Iwai size_t count, loff_t *offset) 3264adb7bcbSTakashi Iwai { 3274adb7bcbSTakashi Iwai struct seq_file *m = file->private_data; 3284adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private; 3294adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 3304adb7bcbSTakashi Iwai struct snd_info_buffer *buf; 3314adb7bcbSTakashi Iwai loff_t pos; 3324adb7bcbSTakashi Iwai size_t next; 3334adb7bcbSTakashi Iwai int err = 0; 3344adb7bcbSTakashi Iwai 3354adb7bcbSTakashi Iwai pos = *offset; 3364adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 3374adb7bcbSTakashi Iwai return -EIO; 3384adb7bcbSTakashi Iwai next = pos + count; 3394adb7bcbSTakashi Iwai mutex_lock(&entry->access); 3404adb7bcbSTakashi Iwai buf = data->wbuffer; 3414adb7bcbSTakashi Iwai if (!buf) { 3424adb7bcbSTakashi Iwai data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3434adb7bcbSTakashi Iwai if (!buf) { 3444adb7bcbSTakashi Iwai err = -ENOMEM; 3454adb7bcbSTakashi Iwai goto error; 3464adb7bcbSTakashi Iwai } 3474adb7bcbSTakashi Iwai } 3484adb7bcbSTakashi Iwai if (next > buf->len) { 3494adb7bcbSTakashi Iwai char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next), 3504adb7bcbSTakashi Iwai GFP_KERNEL | __GFP_ZERO); 3514adb7bcbSTakashi Iwai if (!nbuf) { 3524adb7bcbSTakashi Iwai err = -ENOMEM; 3534adb7bcbSTakashi Iwai goto error; 3544adb7bcbSTakashi Iwai } 3554adb7bcbSTakashi Iwai buf->buffer = nbuf; 3564adb7bcbSTakashi Iwai buf->len = PAGE_ALIGN(next); 3574adb7bcbSTakashi Iwai } 3584adb7bcbSTakashi Iwai if (copy_from_user(buf->buffer + pos, buffer, count)) { 3594adb7bcbSTakashi Iwai err = -EFAULT; 3604adb7bcbSTakashi Iwai goto error; 3614adb7bcbSTakashi Iwai } 3624adb7bcbSTakashi Iwai buf->size = next; 3634adb7bcbSTakashi Iwai error: 3644adb7bcbSTakashi Iwai mutex_unlock(&entry->access); 3654adb7bcbSTakashi Iwai if (err < 0) 3664adb7bcbSTakashi Iwai return err; 3674adb7bcbSTakashi Iwai *offset = next; 3684adb7bcbSTakashi Iwai return count; 3694adb7bcbSTakashi Iwai } 3704adb7bcbSTakashi Iwai 3714adb7bcbSTakashi Iwai static int snd_info_seq_show(struct seq_file *seq, void *p) 3724adb7bcbSTakashi Iwai { 3734adb7bcbSTakashi Iwai struct snd_info_private_data *data = seq->private; 3744adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 3754adb7bcbSTakashi Iwai 3764adb7bcbSTakashi Iwai if (entry->c.text.read) { 3774adb7bcbSTakashi Iwai data->rbuffer->buffer = (char *)seq; /* XXX hack! */ 3784adb7bcbSTakashi Iwai entry->c.text.read(entry, data->rbuffer); 3794adb7bcbSTakashi Iwai } 3804adb7bcbSTakashi Iwai return 0; 3814adb7bcbSTakashi Iwai } 3824adb7bcbSTakashi Iwai 3834adb7bcbSTakashi Iwai static int snd_info_text_entry_open(struct inode *inode, struct file *file) 3844adb7bcbSTakashi Iwai { 3854adb7bcbSTakashi Iwai struct snd_info_entry *entry = PDE_DATA(inode); 3864adb7bcbSTakashi Iwai struct snd_info_private_data *data; 3874adb7bcbSTakashi Iwai int err; 3884adb7bcbSTakashi Iwai 3894adb7bcbSTakashi Iwai mutex_lock(&info_mutex); 3904adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data); 3914adb7bcbSTakashi Iwai if (err < 0) 3924adb7bcbSTakashi Iwai goto unlock; 3934adb7bcbSTakashi Iwai 3944adb7bcbSTakashi Iwai data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL); 3954adb7bcbSTakashi Iwai if (!data->rbuffer) { 3964adb7bcbSTakashi Iwai err = -ENOMEM; 3974adb7bcbSTakashi Iwai goto error; 3984adb7bcbSTakashi Iwai } 3994adb7bcbSTakashi Iwai if (entry->size) 4004adb7bcbSTakashi Iwai err = single_open_size(file, snd_info_seq_show, data, 4014adb7bcbSTakashi Iwai entry->size); 4024adb7bcbSTakashi Iwai else 4034adb7bcbSTakashi Iwai err = single_open(file, snd_info_seq_show, data); 4044adb7bcbSTakashi Iwai if (err < 0) 4054adb7bcbSTakashi Iwai goto error; 4064adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 4074adb7bcbSTakashi Iwai return 0; 4084adb7bcbSTakashi Iwai 4094adb7bcbSTakashi Iwai error: 4104adb7bcbSTakashi Iwai kfree(data->rbuffer); 4114adb7bcbSTakashi Iwai kfree(data); 4124adb7bcbSTakashi Iwai module_put(entry->module); 4134adb7bcbSTakashi Iwai unlock: 4144adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 4154adb7bcbSTakashi Iwai return err; 4164adb7bcbSTakashi Iwai } 4174adb7bcbSTakashi Iwai 4184adb7bcbSTakashi Iwai static int snd_info_text_entry_release(struct inode *inode, struct file *file) 4194adb7bcbSTakashi Iwai { 4204adb7bcbSTakashi Iwai struct seq_file *m = file->private_data; 4214adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private; 4224adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 4234adb7bcbSTakashi Iwai 4244adb7bcbSTakashi Iwai if (data->wbuffer && entry->c.text.write) 4254adb7bcbSTakashi Iwai entry->c.text.write(entry, data->wbuffer); 4264adb7bcbSTakashi Iwai 4274adb7bcbSTakashi Iwai single_release(inode, file); 4284adb7bcbSTakashi Iwai kfree(data->rbuffer); 4294adb7bcbSTakashi Iwai if (data->wbuffer) { 4304adb7bcbSTakashi Iwai kfree(data->wbuffer->buffer); 4314adb7bcbSTakashi Iwai kfree(data->wbuffer); 4324adb7bcbSTakashi Iwai } 4334adb7bcbSTakashi Iwai 4344adb7bcbSTakashi Iwai module_put(entry->module); 4354adb7bcbSTakashi Iwai kfree(data); 4364adb7bcbSTakashi Iwai return 0; 4374adb7bcbSTakashi Iwai } 4384adb7bcbSTakashi Iwai 4394adb7bcbSTakashi Iwai static const struct file_operations snd_info_text_entry_ops = 4404adb7bcbSTakashi Iwai { 4414adb7bcbSTakashi Iwai .owner = THIS_MODULE, 4424adb7bcbSTakashi Iwai .open = snd_info_text_entry_open, 4434adb7bcbSTakashi Iwai .release = snd_info_text_entry_release, 4444adb7bcbSTakashi Iwai .write = snd_info_text_entry_write, 4454adb7bcbSTakashi Iwai .llseek = seq_lseek, 4464adb7bcbSTakashi Iwai .read = seq_read, 4474adb7bcbSTakashi Iwai }; 4484adb7bcbSTakashi Iwai 449886364f6STakashi Iwai static struct snd_info_entry *create_subdir(struct module *mod, 450886364f6STakashi Iwai const char *name) 451886364f6STakashi Iwai { 452886364f6STakashi Iwai struct snd_info_entry *entry; 453886364f6STakashi Iwai 454886364f6STakashi Iwai entry = snd_info_create_module_entry(mod, name, NULL); 455886364f6STakashi Iwai if (!entry) 456886364f6STakashi Iwai return NULL; 457886364f6STakashi Iwai entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 458886364f6STakashi Iwai if (snd_info_register(entry) < 0) { 459886364f6STakashi Iwai snd_info_free_entry(entry); 460886364f6STakashi Iwai return NULL; 461886364f6STakashi Iwai } 462886364f6STakashi Iwai return entry; 463886364f6STakashi Iwai } 464886364f6STakashi Iwai 4651da177e4SLinus Torvalds int __init snd_info_init(void) 4661da177e4SLinus Torvalds { 4671da177e4SLinus Torvalds struct proc_dir_entry *p; 4681da177e4SLinus Torvalds 469e55d92b9SAl Viro p = proc_mkdir("asound", NULL); 4701da177e4SLinus Torvalds if (p == NULL) 4711da177e4SLinus Torvalds return -ENOMEM; 4721da177e4SLinus Torvalds snd_proc_root = p; 4731da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 474886364f6STakashi Iwai snd_oss_root = create_subdir(THIS_MODULE, "oss"); 475886364f6STakashi Iwai if (!snd_oss_root) 476886364f6STakashi Iwai goto error; 4771da177e4SLinus Torvalds #endif 4788eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER) 479886364f6STakashi Iwai snd_seq_root = create_subdir(THIS_MODULE, "seq"); 480886364f6STakashi Iwai if (!snd_seq_root) 481886364f6STakashi Iwai goto error; 4821da177e4SLinus Torvalds #endif 4831da177e4SLinus Torvalds snd_info_version_init(); 4841da177e4SLinus Torvalds snd_minor_info_init(); 4851da177e4SLinus Torvalds snd_minor_info_oss_init(); 4861da177e4SLinus Torvalds snd_card_info_init(); 4871da177e4SLinus Torvalds return 0; 488886364f6STakashi Iwai 489886364f6STakashi Iwai error: 490886364f6STakashi Iwai #ifdef CONFIG_SND_OSSEMUL 491886364f6STakashi Iwai snd_info_free_entry(snd_oss_root); 492886364f6STakashi Iwai #endif 493886364f6STakashi Iwai proc_remove(snd_proc_root); 494886364f6STakashi Iwai return -ENOMEM; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds int __exit snd_info_done(void) 4981da177e4SLinus Torvalds { 4991da177e4SLinus Torvalds snd_card_info_done(); 5001da177e4SLinus Torvalds snd_minor_info_oss_done(); 5011da177e4SLinus Torvalds snd_minor_info_done(); 5021da177e4SLinus Torvalds snd_info_version_done(); 5031da177e4SLinus Torvalds if (snd_proc_root) { 5048eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER) 505746d4a02STakashi Iwai snd_info_free_entry(snd_seq_root); 5061da177e4SLinus Torvalds #endif 5071da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 508746d4a02STakashi Iwai snd_info_free_entry(snd_oss_root); 5091da177e4SLinus Torvalds #endif 510a8ca16eaSDavid Howells proc_remove(snd_proc_root); 5111da177e4SLinus Torvalds } 5121da177e4SLinus Torvalds return 0; 5131da177e4SLinus Torvalds } 5141da177e4SLinus Torvalds 5151da177e4SLinus Torvalds /* 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds */ 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds 5201da177e4SLinus Torvalds /* 5211da177e4SLinus Torvalds * create a card proc file 5221da177e4SLinus Torvalds * called from init.c 5231da177e4SLinus Torvalds */ 52424c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card) 5251da177e4SLinus Torvalds { 5261da177e4SLinus Torvalds char str[8]; 52724c1f931STakashi Iwai struct snd_info_entry *entry; 5281da177e4SLinus Torvalds 5297eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 5307eaa943cSTakashi Iwai return -ENXIO; 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds sprintf(str, "card%i", card->number); 533886364f6STakashi Iwai entry = create_subdir(card->module, str); 534886364f6STakashi Iwai if (!entry) 5351da177e4SLinus Torvalds return -ENOMEM; 5361da177e4SLinus Torvalds card->proc_root = entry; 5371da177e4SLinus Torvalds return 0; 5381da177e4SLinus Torvalds } 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds /* 5411da177e4SLinus Torvalds * register the card proc file 5421da177e4SLinus Torvalds * called from init.c 5431da177e4SLinus Torvalds */ 54424c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card) 5451da177e4SLinus Torvalds { 5461da177e4SLinus Torvalds struct proc_dir_entry *p; 5471da177e4SLinus Torvalds 5487eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 5497eaa943cSTakashi Iwai return -ENXIO; 5501da177e4SLinus Torvalds 5511da177e4SLinus Torvalds if (!strcmp(card->id, card->proc_root->name)) 5521da177e4SLinus Torvalds return 0; 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds p = proc_symlink(card->id, snd_proc_root, card->proc_root->name); 5551da177e4SLinus Torvalds if (p == NULL) 5561da177e4SLinus Torvalds return -ENOMEM; 5571da177e4SLinus Torvalds card->proc_root_link = p; 5581da177e4SLinus Torvalds return 0; 5591da177e4SLinus Torvalds } 5601da177e4SLinus Torvalds 5611da177e4SLinus Torvalds /* 562c2eb9c4eSJaroslav Kysela * called on card->id change 563c2eb9c4eSJaroslav Kysela */ 564c2eb9c4eSJaroslav Kysela void snd_info_card_id_change(struct snd_card *card) 565c2eb9c4eSJaroslav Kysela { 566c2eb9c4eSJaroslav Kysela mutex_lock(&info_mutex); 567c2eb9c4eSJaroslav Kysela if (card->proc_root_link) { 568a8ca16eaSDavid Howells proc_remove(card->proc_root_link); 569c2eb9c4eSJaroslav Kysela card->proc_root_link = NULL; 570c2eb9c4eSJaroslav Kysela } 571c2eb9c4eSJaroslav Kysela if (strcmp(card->id, card->proc_root->name)) 572c2eb9c4eSJaroslav Kysela card->proc_root_link = proc_symlink(card->id, 573c2eb9c4eSJaroslav Kysela snd_proc_root, 574c2eb9c4eSJaroslav Kysela card->proc_root->name); 575c2eb9c4eSJaroslav Kysela mutex_unlock(&info_mutex); 576c2eb9c4eSJaroslav Kysela } 577c2eb9c4eSJaroslav Kysela 578c2eb9c4eSJaroslav Kysela /* 5791da177e4SLinus Torvalds * de-register the card proc file 5801da177e4SLinus Torvalds * called from init.c 5811da177e4SLinus Torvalds */ 582746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card) 5831da177e4SLinus Torvalds { 5847eaa943cSTakashi Iwai if (!card) 5857eaa943cSTakashi Iwai return; 586746d4a02STakashi Iwai mutex_lock(&info_mutex); 587a8ca16eaSDavid Howells proc_remove(card->proc_root_link); 5881da177e4SLinus Torvalds card->proc_root_link = NULL; 589746d4a02STakashi Iwai if (card->proc_root) 590746d4a02STakashi Iwai snd_info_disconnect(card->proc_root); 591746d4a02STakashi Iwai mutex_unlock(&info_mutex); 5921da177e4SLinus Torvalds } 593746d4a02STakashi Iwai 594746d4a02STakashi Iwai /* 595746d4a02STakashi Iwai * release the card proc file resources 596746d4a02STakashi Iwai * called from init.c 597746d4a02STakashi Iwai */ 598746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card) 599746d4a02STakashi Iwai { 6007eaa943cSTakashi Iwai if (!card) 6017eaa943cSTakashi Iwai return 0; 602746d4a02STakashi Iwai snd_info_free_entry(card->proc_root); 603746d4a02STakashi Iwai card->proc_root = NULL; 6041da177e4SLinus Torvalds return 0; 6051da177e4SLinus Torvalds } 6061da177e4SLinus Torvalds 6071da177e4SLinus Torvalds 6081da177e4SLinus Torvalds /** 6091da177e4SLinus Torvalds * snd_info_get_line - read one line from the procfs buffer 6101da177e4SLinus Torvalds * @buffer: the procfs buffer 6111da177e4SLinus Torvalds * @line: the buffer to store 612ddc64b27SClemens Ladisch * @len: the max. buffer size 6131da177e4SLinus Torvalds * 6141da177e4SLinus Torvalds * Reads one line from the buffer and stores the string. 6151da177e4SLinus Torvalds * 616eb7c06e8SYacine Belkadi * Return: Zero if successful, or 1 if error or EOF. 6171da177e4SLinus Torvalds */ 61824c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) 6191da177e4SLinus Torvalds { 6201da177e4SLinus Torvalds int c = -1; 6211da177e4SLinus Torvalds 6220bc0ec90STakashi Iwai if (snd_BUG_ON(!buffer || !buffer->buffer)) 6230bc0ec90STakashi Iwai return 1; 6241da177e4SLinus Torvalds if (len <= 0 || buffer->stop || buffer->error) 6251da177e4SLinus Torvalds return 1; 6260bc0ec90STakashi Iwai while (!buffer->stop) { 6277e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++]; 6287e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) 6291da177e4SLinus Torvalds buffer->stop = 1; 6300bc0ec90STakashi Iwai if (c == '\n') 6311da177e4SLinus Torvalds break; 632ddc64b27SClemens Ladisch if (len > 1) { 6330bc0ec90STakashi Iwai len--; 6341da177e4SLinus Torvalds *line++ = c; 6351da177e4SLinus Torvalds } 6361da177e4SLinus Torvalds } 6371da177e4SLinus Torvalds *line = '\0'; 6381da177e4SLinus Torvalds return 0; 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds 641c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line); 642c0d3fb39STakashi Iwai 6431da177e4SLinus Torvalds /** 644856def8aSHenrik Kretzschmar * snd_info_get_str - parse a string token 6451da177e4SLinus Torvalds * @dest: the buffer to store the string token 6461da177e4SLinus Torvalds * @src: the original string 6471da177e4SLinus Torvalds * @len: the max. length of token - 1 6481da177e4SLinus Torvalds * 6491da177e4SLinus Torvalds * Parses the original string and copy a token to the given 6501da177e4SLinus Torvalds * string buffer. 6511da177e4SLinus Torvalds * 652eb7c06e8SYacine Belkadi * Return: The updated pointer of the original string so that 6531da177e4SLinus Torvalds * it can be used for the next call. 6541da177e4SLinus Torvalds */ 6554f7454a9STakashi Iwai const char *snd_info_get_str(char *dest, const char *src, int len) 6561da177e4SLinus Torvalds { 6571da177e4SLinus Torvalds int c; 6581da177e4SLinus Torvalds 6591da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 6601da177e4SLinus Torvalds src++; 6611da177e4SLinus Torvalds if (*src == '"' || *src == '\'') { 6621da177e4SLinus Torvalds c = *src++; 6631da177e4SLinus Torvalds while (--len > 0 && *src && *src != c) { 6641da177e4SLinus Torvalds *dest++ = *src++; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds if (*src == c) 6671da177e4SLinus Torvalds src++; 6681da177e4SLinus Torvalds } else { 6691da177e4SLinus Torvalds while (--len > 0 && *src && *src != ' ' && *src != '\t') { 6701da177e4SLinus Torvalds *dest++ = *src++; 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds } 6731da177e4SLinus Torvalds *dest = 0; 6741da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 6751da177e4SLinus Torvalds src++; 6761da177e4SLinus Torvalds return src; 6771da177e4SLinus Torvalds } 6781da177e4SLinus Torvalds 679c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str); 680c0d3fb39STakashi Iwai 6811da177e4SLinus Torvalds /** 6821da177e4SLinus Torvalds * snd_info_create_entry - create an info entry 6831da177e4SLinus Torvalds * @name: the proc file name 6841da177e4SLinus Torvalds * 6851da177e4SLinus Torvalds * Creates an info entry with the given file name and initializes as 6861da177e4SLinus Torvalds * the default state. 6871da177e4SLinus Torvalds * 6881da177e4SLinus Torvalds * Usually called from other functions such as 6891da177e4SLinus Torvalds * snd_info_create_card_entry(). 6901da177e4SLinus Torvalds * 691eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 6921da177e4SLinus Torvalds */ 69324c1f931STakashi Iwai static struct snd_info_entry *snd_info_create_entry(const char *name) 6941da177e4SLinus Torvalds { 69524c1f931STakashi Iwai struct snd_info_entry *entry; 696ca2c0966STakashi Iwai entry = kzalloc(sizeof(*entry), GFP_KERNEL); 6971da177e4SLinus Torvalds if (entry == NULL) 6981da177e4SLinus Torvalds return NULL; 699543537bdSPaulo Marques entry->name = kstrdup(name, GFP_KERNEL); 7001da177e4SLinus Torvalds if (entry->name == NULL) { 7011da177e4SLinus Torvalds kfree(entry); 7021da177e4SLinus Torvalds return NULL; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds entry->mode = S_IFREG | S_IRUGO; 7051da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_TEXT; 7061a60d4c5SIngo Molnar mutex_init(&entry->access); 707746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->children); 708746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->list); 7091da177e4SLinus Torvalds return entry; 7101da177e4SLinus Torvalds } 7111da177e4SLinus Torvalds 7121da177e4SLinus Torvalds /** 7131da177e4SLinus Torvalds * snd_info_create_module_entry - create an info entry for the given module 7141da177e4SLinus Torvalds * @module: the module pointer 7151da177e4SLinus Torvalds * @name: the file name 7161da177e4SLinus Torvalds * @parent: the parent directory 7171da177e4SLinus Torvalds * 7181da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given module. 7191da177e4SLinus Torvalds * 720eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 7211da177e4SLinus Torvalds */ 72224c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module, 7231da177e4SLinus Torvalds const char *name, 72424c1f931STakashi Iwai struct snd_info_entry *parent) 7251da177e4SLinus Torvalds { 72624c1f931STakashi Iwai struct snd_info_entry *entry = snd_info_create_entry(name); 7271da177e4SLinus Torvalds if (entry) { 7281da177e4SLinus Torvalds entry->module = module; 7291da177e4SLinus Torvalds entry->parent = parent; 7301da177e4SLinus Torvalds } 7311da177e4SLinus Torvalds return entry; 7321da177e4SLinus Torvalds } 7331da177e4SLinus Torvalds 734c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry); 735c0d3fb39STakashi Iwai 7361da177e4SLinus Torvalds /** 7371da177e4SLinus Torvalds * snd_info_create_card_entry - create an info entry for the given card 7381da177e4SLinus Torvalds * @card: the card instance 7391da177e4SLinus Torvalds * @name: the file name 7401da177e4SLinus Torvalds * @parent: the parent directory 7411da177e4SLinus Torvalds * 7421da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card. 7431da177e4SLinus Torvalds * 744eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 7451da177e4SLinus Torvalds */ 74624c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, 7471da177e4SLinus Torvalds const char *name, 74824c1f931STakashi Iwai struct snd_info_entry * parent) 7491da177e4SLinus Torvalds { 75024c1f931STakashi Iwai struct snd_info_entry *entry = snd_info_create_entry(name); 7511da177e4SLinus Torvalds if (entry) { 7521da177e4SLinus Torvalds entry->module = card->module; 7531da177e4SLinus Torvalds entry->card = card; 7541da177e4SLinus Torvalds entry->parent = parent; 7551da177e4SLinus Torvalds } 7561da177e4SLinus Torvalds return entry; 7571da177e4SLinus Torvalds } 7581da177e4SLinus Torvalds 759c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry); 760c0d3fb39STakashi Iwai 761746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry) 762746d4a02STakashi Iwai { 763746d4a02STakashi Iwai struct list_head *p, *n; 764746d4a02STakashi Iwai 765746d4a02STakashi Iwai list_for_each_safe(p, n, &entry->children) { 766746d4a02STakashi Iwai snd_info_disconnect(list_entry(p, struct snd_info_entry, list)); 767746d4a02STakashi Iwai } 768746d4a02STakashi Iwai 769746d4a02STakashi Iwai if (! entry->p) 770746d4a02STakashi Iwai return; 771746d4a02STakashi Iwai list_del_init(&entry->list); 772a8ca16eaSDavid Howells proc_remove(entry->p); 773746d4a02STakashi Iwai entry->p = NULL; 774746d4a02STakashi Iwai } 775746d4a02STakashi Iwai 77624c1f931STakashi Iwai static int snd_info_dev_free_entry(struct snd_device *device) 7771da177e4SLinus Torvalds { 77824c1f931STakashi Iwai struct snd_info_entry *entry = device->device_data; 7791da177e4SLinus Torvalds snd_info_free_entry(entry); 7801da177e4SLinus Torvalds return 0; 7811da177e4SLinus Torvalds } 7821da177e4SLinus Torvalds 78324c1f931STakashi Iwai static int snd_info_dev_register_entry(struct snd_device *device) 7841da177e4SLinus Torvalds { 78524c1f931STakashi Iwai struct snd_info_entry *entry = device->device_data; 7861da177e4SLinus Torvalds return snd_info_register(entry); 7871da177e4SLinus Torvalds } 7881da177e4SLinus Torvalds 7891da177e4SLinus Torvalds /** 7901da177e4SLinus Torvalds * snd_card_proc_new - create an info entry for the given card 7911da177e4SLinus Torvalds * @card: the card instance 7921da177e4SLinus Torvalds * @name: the file name 7931da177e4SLinus Torvalds * @entryp: the pointer to store the new info entry 7941da177e4SLinus Torvalds * 7951da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card. 7961da177e4SLinus Torvalds * Unlike snd_info_create_card_entry(), this function registers the 7971da177e4SLinus Torvalds * info entry as an ALSA device component, so that it can be 7981da177e4SLinus Torvalds * unregistered/released without explicit call. 7991da177e4SLinus Torvalds * Also, you don't have to register this entry via snd_info_register(), 8001da177e4SLinus Torvalds * since this will be registered by snd_card_register() automatically. 8011da177e4SLinus Torvalds * 8021da177e4SLinus Torvalds * The parent is assumed as card->proc_root. 8031da177e4SLinus Torvalds * 8041da177e4SLinus Torvalds * For releasing this entry, use snd_device_free() instead of 8051da177e4SLinus Torvalds * snd_info_free_entry(). 8061da177e4SLinus Torvalds * 807eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 8081da177e4SLinus Torvalds */ 80924c1f931STakashi Iwai int snd_card_proc_new(struct snd_card *card, const char *name, 81024c1f931STakashi Iwai struct snd_info_entry **entryp) 8111da177e4SLinus Torvalds { 81224c1f931STakashi Iwai static struct snd_device_ops ops = { 8131da177e4SLinus Torvalds .dev_free = snd_info_dev_free_entry, 8141da177e4SLinus Torvalds .dev_register = snd_info_dev_register_entry, 815746d4a02STakashi Iwai /* disconnect is done via snd_info_card_disconnect() */ 8161da177e4SLinus Torvalds }; 81724c1f931STakashi Iwai struct snd_info_entry *entry; 8181da177e4SLinus Torvalds int err; 8191da177e4SLinus Torvalds 8201da177e4SLinus Torvalds entry = snd_info_create_card_entry(card, name, card->proc_root); 8211da177e4SLinus Torvalds if (! entry) 8221da177e4SLinus Torvalds return -ENOMEM; 8231da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) { 8241da177e4SLinus Torvalds snd_info_free_entry(entry); 8251da177e4SLinus Torvalds return err; 8261da177e4SLinus Torvalds } 8271da177e4SLinus Torvalds if (entryp) 8281da177e4SLinus Torvalds *entryp = entry; 8291da177e4SLinus Torvalds return 0; 8301da177e4SLinus Torvalds } 8311da177e4SLinus Torvalds 832c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_proc_new); 833c0d3fb39STakashi Iwai 8341da177e4SLinus Torvalds /** 8351da177e4SLinus Torvalds * snd_info_free_entry - release the info entry 8361da177e4SLinus Torvalds * @entry: the info entry 8371da177e4SLinus Torvalds * 8381da177e4SLinus Torvalds * Releases the info entry. Don't call this after registered. 8391da177e4SLinus Torvalds */ 84024c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry) 8411da177e4SLinus Torvalds { 8421da177e4SLinus Torvalds if (entry == NULL) 8431da177e4SLinus Torvalds return; 844746d4a02STakashi Iwai if (entry->p) { 845746d4a02STakashi Iwai mutex_lock(&info_mutex); 846746d4a02STakashi Iwai snd_info_disconnect(entry); 847746d4a02STakashi Iwai mutex_unlock(&info_mutex); 848746d4a02STakashi Iwai } 8491da177e4SLinus Torvalds kfree(entry->name); 8501da177e4SLinus Torvalds if (entry->private_free) 8511da177e4SLinus Torvalds entry->private_free(entry); 8521da177e4SLinus Torvalds kfree(entry); 8531da177e4SLinus Torvalds } 8541da177e4SLinus Torvalds 855c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry); 856c0d3fb39STakashi Iwai 8571da177e4SLinus Torvalds /** 8581da177e4SLinus Torvalds * snd_info_register - register the info entry 8591da177e4SLinus Torvalds * @entry: the info entry 8601da177e4SLinus Torvalds * 8611da177e4SLinus Torvalds * Registers the proc info entry. 8621da177e4SLinus Torvalds * 863eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 8641da177e4SLinus Torvalds */ 86524c1f931STakashi Iwai int snd_info_register(struct snd_info_entry * entry) 8661da177e4SLinus Torvalds { 8671da177e4SLinus Torvalds struct proc_dir_entry *root, *p = NULL; 8681da177e4SLinus Torvalds 8697eaa943cSTakashi Iwai if (snd_BUG_ON(!entry)) 8707eaa943cSTakashi Iwai return -ENXIO; 8711da177e4SLinus Torvalds root = entry->parent == NULL ? snd_proc_root : entry->parent->p; 8721a60d4c5SIngo Molnar mutex_lock(&info_mutex); 873aee0c612SAl Viro if (S_ISDIR(entry->mode)) { 874aee0c612SAl Viro p = proc_mkdir_mode(entry->name, entry->mode, root); 8751da177e4SLinus Torvalds if (!p) { 8761a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 8771da177e4SLinus Torvalds return -ENOMEM; 8781da177e4SLinus Torvalds } 879aee0c612SAl Viro } else { 8804adb7bcbSTakashi Iwai const struct file_operations *ops; 8814adb7bcbSTakashi Iwai if (entry->content == SNDRV_INFO_CONTENT_DATA) 8824adb7bcbSTakashi Iwai ops = &snd_info_entry_operations; 8834adb7bcbSTakashi Iwai else 8844adb7bcbSTakashi Iwai ops = &snd_info_text_entry_ops; 885aee0c612SAl Viro p = proc_create_data(entry->name, entry->mode, root, 8864adb7bcbSTakashi Iwai ops, entry); 887aee0c612SAl Viro if (!p) { 888aee0c612SAl Viro mutex_unlock(&info_mutex); 889aee0c612SAl Viro return -ENOMEM; 890aee0c612SAl Viro } 891271a15eaSDavid Howells proc_set_size(p, entry->size); 892aee0c612SAl Viro } 8931da177e4SLinus Torvalds entry->p = p; 894746d4a02STakashi Iwai if (entry->parent) 895746d4a02STakashi Iwai list_add_tail(&entry->list, &entry->parent->children); 8961a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 8971da177e4SLinus Torvalds return 0; 8981da177e4SLinus Torvalds } 8991da177e4SLinus Torvalds 900c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register); 901c0d3fb39STakashi Iwai 9021da177e4SLinus Torvalds /* 9031da177e4SLinus Torvalds 9041da177e4SLinus Torvalds */ 9051da177e4SLinus Torvalds 9066581f4e7STakashi Iwai static struct snd_info_entry *snd_info_version_entry; 9071da177e4SLinus Torvalds 90824c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 9091da177e4SLinus Torvalds { 9101da177e4SLinus Torvalds snd_iprintf(buffer, 91142662748SJaroslav Kysela "Advanced Linux Sound Architecture Driver Version k%s.\n", 91242662748SJaroslav Kysela init_utsname()->release); 9131da177e4SLinus Torvalds } 9141da177e4SLinus Torvalds 9151da177e4SLinus Torvalds static int __init snd_info_version_init(void) 9161da177e4SLinus Torvalds { 91724c1f931STakashi Iwai struct snd_info_entry *entry; 9181da177e4SLinus Torvalds 9191da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); 9201da177e4SLinus Torvalds if (entry == NULL) 9211da177e4SLinus Torvalds return -ENOMEM; 9221da177e4SLinus Torvalds entry->c.text.read = snd_info_version_read; 9231da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 9241da177e4SLinus Torvalds snd_info_free_entry(entry); 9251da177e4SLinus Torvalds return -ENOMEM; 9261da177e4SLinus Torvalds } 9271da177e4SLinus Torvalds snd_info_version_entry = entry; 9281da177e4SLinus Torvalds return 0; 9291da177e4SLinus Torvalds } 9301da177e4SLinus Torvalds 9311da177e4SLinus Torvalds static int __exit snd_info_version_done(void) 9321da177e4SLinus Torvalds { 933746d4a02STakashi Iwai snd_info_free_entry(snd_info_version_entry); 9341da177e4SLinus Torvalds return 0; 9351da177e4SLinus Torvalds } 9361da177e4SLinus Torvalds 9371da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 938