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 int snd_info_check_reserved_words(const char *str) 371da177e4SLinus Torvalds { 381da177e4SLinus Torvalds static char *reserved[] = 391da177e4SLinus Torvalds { 401da177e4SLinus Torvalds "version", 411da177e4SLinus Torvalds "meminfo", 421da177e4SLinus Torvalds "memdebug", 431da177e4SLinus Torvalds "detect", 441da177e4SLinus Torvalds "devices", 451da177e4SLinus Torvalds "oss", 461da177e4SLinus Torvalds "cards", 471da177e4SLinus Torvalds "timers", 481da177e4SLinus Torvalds "synth", 491da177e4SLinus Torvalds "pcm", 501da177e4SLinus Torvalds "seq", 511da177e4SLinus Torvalds NULL 521da177e4SLinus Torvalds }; 531da177e4SLinus Torvalds char **xstr = reserved; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds while (*xstr) { 561da177e4SLinus Torvalds if (!strcmp(*xstr, str)) 571da177e4SLinus Torvalds return 0; 581da177e4SLinus Torvalds xstr++; 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds if (!strncmp(str, "card", 4)) 611da177e4SLinus Torvalds return 0; 621da177e4SLinus Torvalds return 1; 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds 651a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex); 661da177e4SLinus Torvalds 6724c1f931STakashi Iwai struct snd_info_private_data { 6824c1f931STakashi Iwai struct snd_info_buffer *rbuffer; 6924c1f931STakashi Iwai struct snd_info_buffer *wbuffer; 7024c1f931STakashi Iwai struct snd_info_entry *entry; 711da177e4SLinus Torvalds void *file_private_data; 7224c1f931STakashi Iwai }; 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds static int snd_info_version_init(void); 75746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry); 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds /* 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds */ 801da177e4SLinus Torvalds 81644dbd64STakashi Iwai static struct snd_info_entry *snd_proc_root; 826581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root; 83c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root); 84c0d3fb39STakashi Iwai 851da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 866581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root; 871da177e4SLinus Torvalds #endif 881da177e4SLinus Torvalds 894adb7bcbSTakashi Iwai static int alloc_info_private(struct snd_info_entry *entry, 904adb7bcbSTakashi Iwai struct snd_info_private_data **ret) 914adb7bcbSTakashi Iwai { 924adb7bcbSTakashi Iwai struct snd_info_private_data *data; 934adb7bcbSTakashi Iwai 944adb7bcbSTakashi Iwai if (!entry || !entry->p) 954adb7bcbSTakashi Iwai return -ENODEV; 964adb7bcbSTakashi Iwai if (!try_module_get(entry->module)) 974adb7bcbSTakashi Iwai return -EFAULT; 984adb7bcbSTakashi Iwai data = kzalloc(sizeof(*data), GFP_KERNEL); 994adb7bcbSTakashi Iwai if (!data) { 1004adb7bcbSTakashi Iwai module_put(entry->module); 1014adb7bcbSTakashi Iwai return -ENOMEM; 1024adb7bcbSTakashi Iwai } 1034adb7bcbSTakashi Iwai data->entry = entry; 1044adb7bcbSTakashi Iwai *ret = data; 1054adb7bcbSTakashi Iwai return 0; 1064adb7bcbSTakashi Iwai } 1074adb7bcbSTakashi Iwai 1084adb7bcbSTakashi Iwai static bool valid_pos(loff_t pos, size_t count) 1094adb7bcbSTakashi Iwai { 1104adb7bcbSTakashi Iwai if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 1114adb7bcbSTakashi Iwai return false; 1124adb7bcbSTakashi Iwai if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 1134adb7bcbSTakashi Iwai return false; 1144adb7bcbSTakashi Iwai return true; 1154adb7bcbSTakashi Iwai } 1164adb7bcbSTakashi Iwai 1174adb7bcbSTakashi Iwai /* 1184adb7bcbSTakashi Iwai * file ops for binary proc files 1194adb7bcbSTakashi Iwai */ 1201da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) 1211da177e4SLinus Torvalds { 12224c1f931STakashi Iwai struct snd_info_private_data *data; 1231da177e4SLinus Torvalds struct snd_info_entry *entry; 12473029e0fSTakashi Iwai loff_t ret = -EINVAL, size; 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds data = file->private_data; 1271da177e4SLinus Torvalds entry = data->entry; 1285b5cd553STakashi Iwai mutex_lock(&entry->access); 1294adb7bcbSTakashi Iwai if (entry->c.ops->llseek) { 13073029e0fSTakashi Iwai offset = entry->c.ops->llseek(entry, 1311da177e4SLinus Torvalds data->file_private_data, 1321da177e4SLinus Torvalds file, offset, orig); 1331da177e4SLinus Torvalds goto out; 1341da177e4SLinus Torvalds } 1354adb7bcbSTakashi Iwai 13673029e0fSTakashi Iwai size = entry->size; 13773029e0fSTakashi Iwai switch (orig) { 13873029e0fSTakashi Iwai case SEEK_SET: 1391da177e4SLinus Torvalds break; 14073029e0fSTakashi Iwai case SEEK_CUR: 14173029e0fSTakashi Iwai offset += file->f_pos; 14273029e0fSTakashi Iwai break; 14373029e0fSTakashi Iwai case SEEK_END: 14473029e0fSTakashi Iwai if (!size) 14573029e0fSTakashi Iwai goto out; 14673029e0fSTakashi Iwai offset += size; 14773029e0fSTakashi Iwai break; 14873029e0fSTakashi Iwai default: 14973029e0fSTakashi Iwai goto out; 1501da177e4SLinus Torvalds } 15173029e0fSTakashi Iwai if (offset < 0) 15273029e0fSTakashi Iwai goto out; 15373029e0fSTakashi Iwai if (size && offset > size) 15473029e0fSTakashi Iwai offset = size; 15573029e0fSTakashi Iwai file->f_pos = offset; 15673029e0fSTakashi Iwai ret = offset; 1571da177e4SLinus Torvalds out: 1585b5cd553STakashi Iwai mutex_unlock(&entry->access); 1591da177e4SLinus Torvalds return ret; 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, 1631da177e4SLinus Torvalds size_t count, loff_t * offset) 1641da177e4SLinus Torvalds { 1654adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 1664adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 1674adb7bcbSTakashi Iwai size_t size; 1681da177e4SLinus Torvalds loff_t pos; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds pos = *offset; 1714adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 1721da177e4SLinus Torvalds return -EIO; 173d97e1b78STakashi Iwai if (pos >= entry->size) 174d97e1b78STakashi Iwai return 0; 175d97e1b78STakashi Iwai size = entry->size - pos; 176d97e1b78STakashi Iwai size = min(count, size); 1774adb7bcbSTakashi Iwai size = entry->c.ops->read(entry, data->file_private_data, 178d97e1b78STakashi Iwai file, buffer, size, pos); 1791da177e4SLinus Torvalds if ((ssize_t) size > 0) 1801da177e4SLinus Torvalds *offset = pos + size; 1811da177e4SLinus Torvalds return size; 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, 1851da177e4SLinus Torvalds size_t count, loff_t * offset) 1861da177e4SLinus Torvalds { 1874adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 1884adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 1897e4eeec8STakashi Iwai ssize_t size = 0; 1901da177e4SLinus Torvalds loff_t pos; 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds pos = *offset; 1934adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 1941da177e4SLinus Torvalds return -EIO; 1954adb7bcbSTakashi Iwai if (count > 0) { 196d97e1b78STakashi Iwai size_t maxsize = entry->size - pos; 197d97e1b78STakashi Iwai count = min(count, maxsize); 1984adb7bcbSTakashi Iwai size = entry->c.ops->write(entry, data->file_private_data, 1991da177e4SLinus Torvalds file, buffer, count, pos); 200d97e1b78STakashi Iwai } 2014adb7bcbSTakashi Iwai if (size > 0) 2021da177e4SLinus Torvalds *offset = pos + size; 2031da177e4SLinus Torvalds return size; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 206680ef72aSAl Viro static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait) 2071da177e4SLinus Torvalds { 2084adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2094adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 210680ef72aSAl Viro __poll_t mask = 0; 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds if (entry->c.ops->poll) 2131da177e4SLinus Torvalds return entry->c.ops->poll(entry, 2141da177e4SLinus Torvalds data->file_private_data, 2151da177e4SLinus Torvalds file, wait); 2161da177e4SLinus Torvalds if (entry->c.ops->read) 217a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM; 2181da177e4SLinus Torvalds if (entry->c.ops->write) 219a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM; 2201da177e4SLinus Torvalds return mask; 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds 223d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, 224d99e9889SIngo Molnar unsigned long arg) 2251da177e4SLinus Torvalds { 2264adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2274adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 2281da177e4SLinus Torvalds 2294adb7bcbSTakashi Iwai if (!entry->c.ops->ioctl) 2301da177e4SLinus Torvalds return -ENOTTY; 2314adb7bcbSTakashi Iwai return entry->c.ops->ioctl(entry, data->file_private_data, 2324adb7bcbSTakashi Iwai file, cmd, arg); 2331da177e4SLinus Torvalds } 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) 2361da177e4SLinus Torvalds { 237496ad9aaSAl Viro struct inode *inode = file_inode(file); 23824c1f931STakashi Iwai struct snd_info_private_data *data; 2391da177e4SLinus Torvalds struct snd_info_entry *entry; 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds data = file->private_data; 2421da177e4SLinus Torvalds if (data == NULL) 2431da177e4SLinus Torvalds return 0; 2441da177e4SLinus Torvalds entry = data->entry; 2454adb7bcbSTakashi Iwai if (!entry->c.ops->mmap) 2461da177e4SLinus Torvalds return -ENXIO; 2474adb7bcbSTakashi Iwai return entry->c.ops->mmap(entry, data->file_private_data, 2484adb7bcbSTakashi Iwai inode, file, vma); 2494adb7bcbSTakashi Iwai } 2504adb7bcbSTakashi Iwai 2514adb7bcbSTakashi Iwai static int snd_info_entry_open(struct inode *inode, struct file *file) 2524adb7bcbSTakashi Iwai { 2534adb7bcbSTakashi Iwai struct snd_info_entry *entry = PDE_DATA(inode); 2544adb7bcbSTakashi Iwai struct snd_info_private_data *data; 2554adb7bcbSTakashi Iwai int mode, err; 2564adb7bcbSTakashi Iwai 2574adb7bcbSTakashi Iwai mutex_lock(&info_mutex); 2584adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data); 2594adb7bcbSTakashi Iwai if (err < 0) 2604adb7bcbSTakashi Iwai goto unlock; 2614adb7bcbSTakashi Iwai 2624adb7bcbSTakashi Iwai mode = file->f_flags & O_ACCMODE; 2634adb7bcbSTakashi Iwai if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) || 2644adb7bcbSTakashi Iwai ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) { 2654adb7bcbSTakashi Iwai err = -ENODEV; 2664adb7bcbSTakashi Iwai goto error; 2674adb7bcbSTakashi Iwai } 2684adb7bcbSTakashi Iwai 2694adb7bcbSTakashi Iwai if (entry->c.ops->open) { 2704adb7bcbSTakashi Iwai err = entry->c.ops->open(entry, mode, &data->file_private_data); 2714adb7bcbSTakashi Iwai if (err < 0) 2724adb7bcbSTakashi Iwai goto error; 2734adb7bcbSTakashi Iwai } 2744adb7bcbSTakashi Iwai 2754adb7bcbSTakashi Iwai file->private_data = data; 2764adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 2774adb7bcbSTakashi Iwai return 0; 2784adb7bcbSTakashi Iwai 2794adb7bcbSTakashi Iwai error: 2804adb7bcbSTakashi Iwai kfree(data); 2814adb7bcbSTakashi Iwai module_put(entry->module); 2824adb7bcbSTakashi Iwai unlock: 2834adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 2844adb7bcbSTakashi Iwai return err; 2854adb7bcbSTakashi Iwai } 2864adb7bcbSTakashi Iwai 2874adb7bcbSTakashi Iwai static int snd_info_entry_release(struct inode *inode, struct file *file) 2884adb7bcbSTakashi Iwai { 2894adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2904adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 2914adb7bcbSTakashi Iwai 2924adb7bcbSTakashi Iwai if (entry->c.ops->release) 2934adb7bcbSTakashi Iwai entry->c.ops->release(entry, file->f_flags & O_ACCMODE, 2944adb7bcbSTakashi Iwai data->file_private_data); 2954adb7bcbSTakashi Iwai module_put(entry->module); 2964adb7bcbSTakashi Iwai kfree(data); 2974adb7bcbSTakashi Iwai return 0; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3009c2e08c5SArjan van de Ven static const struct file_operations snd_info_entry_operations = 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds .owner = THIS_MODULE, 3031da177e4SLinus Torvalds .llseek = snd_info_entry_llseek, 3041da177e4SLinus Torvalds .read = snd_info_entry_read, 3051da177e4SLinus Torvalds .write = snd_info_entry_write, 3061da177e4SLinus Torvalds .poll = snd_info_entry_poll, 307d99e9889SIngo Molnar .unlocked_ioctl = snd_info_entry_ioctl, 3081da177e4SLinus Torvalds .mmap = snd_info_entry_mmap, 3091da177e4SLinus Torvalds .open = snd_info_entry_open, 3101da177e4SLinus Torvalds .release = snd_info_entry_release, 3111da177e4SLinus Torvalds }; 3121da177e4SLinus Torvalds 3134adb7bcbSTakashi Iwai /* 3144adb7bcbSTakashi Iwai * file ops for text proc files 3154adb7bcbSTakashi Iwai */ 3164adb7bcbSTakashi Iwai static ssize_t snd_info_text_entry_write(struct file *file, 3174adb7bcbSTakashi Iwai const char __user *buffer, 3184adb7bcbSTakashi Iwai size_t count, loff_t *offset) 3194adb7bcbSTakashi Iwai { 3204adb7bcbSTakashi Iwai struct seq_file *m = file->private_data; 3214adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private; 3224adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 3234adb7bcbSTakashi Iwai struct snd_info_buffer *buf; 3244adb7bcbSTakashi Iwai loff_t pos; 3254adb7bcbSTakashi Iwai size_t next; 3264adb7bcbSTakashi Iwai int err = 0; 3274adb7bcbSTakashi Iwai 3286809cd68STakashi Iwai if (!entry->c.text.write) 3296809cd68STakashi Iwai return -EIO; 3304adb7bcbSTakashi Iwai pos = *offset; 3314adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 3324adb7bcbSTakashi Iwai return -EIO; 3334adb7bcbSTakashi Iwai next = pos + count; 334027a9fe6STakashi Iwai /* don't handle too large text inputs */ 335027a9fe6STakashi Iwai if (next > 16 * 1024) 336027a9fe6STakashi Iwai return -EIO; 3374adb7bcbSTakashi Iwai mutex_lock(&entry->access); 3384adb7bcbSTakashi Iwai buf = data->wbuffer; 3394adb7bcbSTakashi Iwai if (!buf) { 3404adb7bcbSTakashi Iwai data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3414adb7bcbSTakashi Iwai if (!buf) { 3424adb7bcbSTakashi Iwai err = -ENOMEM; 3434adb7bcbSTakashi Iwai goto error; 3444adb7bcbSTakashi Iwai } 3454adb7bcbSTakashi Iwai } 3464adb7bcbSTakashi Iwai if (next > buf->len) { 347ffb73b08STakashi Iwai char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL); 3484adb7bcbSTakashi Iwai if (!nbuf) { 3494adb7bcbSTakashi Iwai err = -ENOMEM; 3504adb7bcbSTakashi Iwai goto error; 3514adb7bcbSTakashi Iwai } 352ffb73b08STakashi Iwai kvfree(buf->buffer); 3534adb7bcbSTakashi Iwai buf->buffer = nbuf; 3544adb7bcbSTakashi Iwai buf->len = PAGE_ALIGN(next); 3554adb7bcbSTakashi Iwai } 3564adb7bcbSTakashi Iwai if (copy_from_user(buf->buffer + pos, buffer, count)) { 3574adb7bcbSTakashi Iwai err = -EFAULT; 3584adb7bcbSTakashi Iwai goto error; 3594adb7bcbSTakashi Iwai } 3604adb7bcbSTakashi Iwai buf->size = next; 3614adb7bcbSTakashi Iwai error: 3624adb7bcbSTakashi Iwai mutex_unlock(&entry->access); 3634adb7bcbSTakashi Iwai if (err < 0) 3644adb7bcbSTakashi Iwai return err; 3654adb7bcbSTakashi Iwai *offset = next; 3664adb7bcbSTakashi Iwai return count; 3674adb7bcbSTakashi Iwai } 3684adb7bcbSTakashi Iwai 3694adb7bcbSTakashi Iwai static int snd_info_seq_show(struct seq_file *seq, void *p) 3704adb7bcbSTakashi Iwai { 3714adb7bcbSTakashi Iwai struct snd_info_private_data *data = seq->private; 3724adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 3734adb7bcbSTakashi Iwai 3746809cd68STakashi Iwai if (!entry->c.text.read) { 3756809cd68STakashi Iwai return -EIO; 3766809cd68STakashi Iwai } else { 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) { 430ffb73b08STakashi Iwai kvfree(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; 4576a73cf46SJoe Perches entry->mode = S_IFDIR | 0555; 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 4658e7ccb7bSTakashi Iwai static struct snd_info_entry * 4668e7ccb7bSTakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent); 467644dbd64STakashi Iwai 4681da177e4SLinus Torvalds int __init snd_info_init(void) 4691da177e4SLinus Torvalds { 4708e7ccb7bSTakashi Iwai snd_proc_root = snd_info_create_entry("asound", NULL); 471644dbd64STakashi Iwai if (!snd_proc_root) 4721da177e4SLinus Torvalds return -ENOMEM; 4736a73cf46SJoe Perches snd_proc_root->mode = S_IFDIR | 0555; 474644dbd64STakashi Iwai snd_proc_root->p = proc_mkdir("asound", NULL); 475644dbd64STakashi Iwai if (!snd_proc_root->p) 476644dbd64STakashi Iwai goto error; 4771da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 478886364f6STakashi Iwai snd_oss_root = create_subdir(THIS_MODULE, "oss"); 479886364f6STakashi Iwai if (!snd_oss_root) 480886364f6STakashi Iwai goto error; 4811da177e4SLinus Torvalds #endif 4828eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER) 483886364f6STakashi Iwai snd_seq_root = create_subdir(THIS_MODULE, "seq"); 484886364f6STakashi Iwai if (!snd_seq_root) 485886364f6STakashi Iwai goto error; 4861da177e4SLinus Torvalds #endif 487b591b6e9STakashi Iwai if (snd_info_version_init() < 0 || 488b591b6e9STakashi Iwai snd_minor_info_init() < 0 || 489b591b6e9STakashi Iwai snd_minor_info_oss_init() < 0 || 490a0dca822STakashi Iwai snd_card_info_init() < 0 || 491a0dca822STakashi Iwai snd_info_minor_register() < 0) 492b591b6e9STakashi Iwai goto error; 4931da177e4SLinus Torvalds return 0; 494886364f6STakashi Iwai 495886364f6STakashi Iwai error: 496644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root); 497886364f6STakashi Iwai return -ENOMEM; 4981da177e4SLinus Torvalds } 4991da177e4SLinus Torvalds 5001da177e4SLinus Torvalds int __exit snd_info_done(void) 5011da177e4SLinus Torvalds { 502644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root); 5031da177e4SLinus Torvalds return 0; 5041da177e4SLinus Torvalds } 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds /* 5071da177e4SLinus Torvalds * create a card proc file 5081da177e4SLinus Torvalds * called from init.c 5091da177e4SLinus Torvalds */ 51024c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card) 5111da177e4SLinus Torvalds { 5121da177e4SLinus Torvalds char str[8]; 51324c1f931STakashi Iwai struct snd_info_entry *entry; 5141da177e4SLinus Torvalds 5157eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 5167eaa943cSTakashi Iwai return -ENXIO; 5171da177e4SLinus Torvalds 5181da177e4SLinus Torvalds sprintf(str, "card%i", card->number); 519886364f6STakashi Iwai entry = create_subdir(card->module, str); 520886364f6STakashi Iwai if (!entry) 5211da177e4SLinus Torvalds return -ENOMEM; 5221da177e4SLinus Torvalds card->proc_root = entry; 5231da177e4SLinus Torvalds return 0; 5241da177e4SLinus Torvalds } 5251da177e4SLinus Torvalds 5262471b6c8STakashi Iwai /* register all pending info entries */ 5272471b6c8STakashi Iwai static int snd_info_register_recursive(struct snd_info_entry *entry) 5282471b6c8STakashi Iwai { 5292471b6c8STakashi Iwai struct snd_info_entry *p; 5302471b6c8STakashi Iwai int err; 5312471b6c8STakashi Iwai 5322471b6c8STakashi Iwai if (!entry->p) { 5332471b6c8STakashi Iwai err = snd_info_register(entry); 5342471b6c8STakashi Iwai if (err < 0) 5352471b6c8STakashi Iwai return err; 5362471b6c8STakashi Iwai } 5372471b6c8STakashi Iwai 5382471b6c8STakashi Iwai list_for_each_entry(p, &entry->children, list) { 5392471b6c8STakashi Iwai err = snd_info_register_recursive(p); 5402471b6c8STakashi Iwai if (err < 0) 5412471b6c8STakashi Iwai return err; 5422471b6c8STakashi Iwai } 5432471b6c8STakashi Iwai 5442471b6c8STakashi Iwai return 0; 5452471b6c8STakashi Iwai } 5462471b6c8STakashi Iwai 5471da177e4SLinus Torvalds /* 5481da177e4SLinus Torvalds * register the card proc file 5491da177e4SLinus Torvalds * called from init.c 5502471b6c8STakashi Iwai * can be called multiple times for reinitialization 5511da177e4SLinus Torvalds */ 55224c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card) 5531da177e4SLinus Torvalds { 5541da177e4SLinus Torvalds struct proc_dir_entry *p; 5552471b6c8STakashi Iwai int err; 5561da177e4SLinus Torvalds 5577eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 5587eaa943cSTakashi Iwai return -ENXIO; 5591da177e4SLinus Torvalds 5602471b6c8STakashi Iwai err = snd_info_register_recursive(card->proc_root); 5612471b6c8STakashi Iwai if (err < 0) 5622471b6c8STakashi Iwai return err; 5632471b6c8STakashi Iwai 5641da177e4SLinus Torvalds if (!strcmp(card->id, card->proc_root->name)) 5651da177e4SLinus Torvalds return 0; 5661da177e4SLinus Torvalds 5672471b6c8STakashi Iwai if (card->proc_root_link) 5682471b6c8STakashi Iwai return 0; 569644dbd64STakashi Iwai p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name); 5702471b6c8STakashi Iwai if (!p) 5711da177e4SLinus Torvalds return -ENOMEM; 5721da177e4SLinus Torvalds card->proc_root_link = p; 5731da177e4SLinus Torvalds return 0; 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds 5761da177e4SLinus Torvalds /* 577c2eb9c4eSJaroslav Kysela * called on card->id change 578c2eb9c4eSJaroslav Kysela */ 579c2eb9c4eSJaroslav Kysela void snd_info_card_id_change(struct snd_card *card) 580c2eb9c4eSJaroslav Kysela { 581c2eb9c4eSJaroslav Kysela mutex_lock(&info_mutex); 582c2eb9c4eSJaroslav Kysela if (card->proc_root_link) { 583a8ca16eaSDavid Howells proc_remove(card->proc_root_link); 584c2eb9c4eSJaroslav Kysela card->proc_root_link = NULL; 585c2eb9c4eSJaroslav Kysela } 586c2eb9c4eSJaroslav Kysela if (strcmp(card->id, card->proc_root->name)) 587c2eb9c4eSJaroslav Kysela card->proc_root_link = proc_symlink(card->id, 588644dbd64STakashi Iwai snd_proc_root->p, 589c2eb9c4eSJaroslav Kysela card->proc_root->name); 590c2eb9c4eSJaroslav Kysela mutex_unlock(&info_mutex); 591c2eb9c4eSJaroslav Kysela } 592c2eb9c4eSJaroslav Kysela 593c2eb9c4eSJaroslav Kysela /* 5941da177e4SLinus Torvalds * de-register the card proc file 5951da177e4SLinus Torvalds * called from init.c 5961da177e4SLinus Torvalds */ 597746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card) 5981da177e4SLinus Torvalds { 5997eaa943cSTakashi Iwai if (!card) 6007eaa943cSTakashi Iwai return; 601746d4a02STakashi Iwai mutex_lock(&info_mutex); 602a8ca16eaSDavid Howells proc_remove(card->proc_root_link); 6031da177e4SLinus Torvalds card->proc_root_link = NULL; 604746d4a02STakashi Iwai if (card->proc_root) 605746d4a02STakashi Iwai snd_info_disconnect(card->proc_root); 606746d4a02STakashi Iwai mutex_unlock(&info_mutex); 6071da177e4SLinus Torvalds } 608746d4a02STakashi Iwai 609746d4a02STakashi Iwai /* 610746d4a02STakashi Iwai * release the card proc file resources 611746d4a02STakashi Iwai * called from init.c 612746d4a02STakashi Iwai */ 613746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card) 614746d4a02STakashi Iwai { 6157eaa943cSTakashi Iwai if (!card) 6167eaa943cSTakashi Iwai return 0; 617746d4a02STakashi Iwai snd_info_free_entry(card->proc_root); 618746d4a02STakashi Iwai card->proc_root = NULL; 6191da177e4SLinus Torvalds return 0; 6201da177e4SLinus Torvalds } 6211da177e4SLinus Torvalds 6221da177e4SLinus Torvalds 6231da177e4SLinus Torvalds /** 6241da177e4SLinus Torvalds * snd_info_get_line - read one line from the procfs buffer 6251da177e4SLinus Torvalds * @buffer: the procfs buffer 6261da177e4SLinus Torvalds * @line: the buffer to store 627ddc64b27SClemens Ladisch * @len: the max. buffer size 6281da177e4SLinus Torvalds * 6291da177e4SLinus Torvalds * Reads one line from the buffer and stores the string. 6301da177e4SLinus Torvalds * 631eb7c06e8SYacine Belkadi * Return: Zero if successful, or 1 if error or EOF. 6321da177e4SLinus Torvalds */ 63324c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) 6341da177e4SLinus Torvalds { 6351da177e4SLinus Torvalds int c = -1; 6361da177e4SLinus Torvalds 6370bc0ec90STakashi Iwai if (snd_BUG_ON(!buffer || !buffer->buffer)) 6380bc0ec90STakashi Iwai return 1; 6391da177e4SLinus Torvalds if (len <= 0 || buffer->stop || buffer->error) 6401da177e4SLinus Torvalds return 1; 6410bc0ec90STakashi Iwai while (!buffer->stop) { 6427e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++]; 6437e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) 6441da177e4SLinus Torvalds buffer->stop = 1; 6450bc0ec90STakashi Iwai if (c == '\n') 6461da177e4SLinus Torvalds break; 647ddc64b27SClemens Ladisch if (len > 1) { 6480bc0ec90STakashi Iwai len--; 6491da177e4SLinus Torvalds *line++ = c; 6501da177e4SLinus Torvalds } 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds *line = '\0'; 6531da177e4SLinus Torvalds return 0; 6541da177e4SLinus Torvalds } 655c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line); 656c0d3fb39STakashi Iwai 6571da177e4SLinus Torvalds /** 658856def8aSHenrik Kretzschmar * snd_info_get_str - parse a string token 6591da177e4SLinus Torvalds * @dest: the buffer to store the string token 6601da177e4SLinus Torvalds * @src: the original string 6611da177e4SLinus Torvalds * @len: the max. length of token - 1 6621da177e4SLinus Torvalds * 6631da177e4SLinus Torvalds * Parses the original string and copy a token to the given 6641da177e4SLinus Torvalds * string buffer. 6651da177e4SLinus Torvalds * 666eb7c06e8SYacine Belkadi * Return: The updated pointer of the original string so that 6671da177e4SLinus Torvalds * it can be used for the next call. 6681da177e4SLinus Torvalds */ 6694f7454a9STakashi Iwai const char *snd_info_get_str(char *dest, const char *src, int len) 6701da177e4SLinus Torvalds { 6711da177e4SLinus Torvalds int c; 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 6741da177e4SLinus Torvalds src++; 6751da177e4SLinus Torvalds if (*src == '"' || *src == '\'') { 6761da177e4SLinus Torvalds c = *src++; 6771da177e4SLinus Torvalds while (--len > 0 && *src && *src != c) { 6781da177e4SLinus Torvalds *dest++ = *src++; 6791da177e4SLinus Torvalds } 6801da177e4SLinus Torvalds if (*src == c) 6811da177e4SLinus Torvalds src++; 6821da177e4SLinus Torvalds } else { 6831da177e4SLinus Torvalds while (--len > 0 && *src && *src != ' ' && *src != '\t') { 6841da177e4SLinus Torvalds *dest++ = *src++; 6851da177e4SLinus Torvalds } 6861da177e4SLinus Torvalds } 6871da177e4SLinus Torvalds *dest = 0; 6881da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 6891da177e4SLinus Torvalds src++; 6901da177e4SLinus Torvalds return src; 6911da177e4SLinus Torvalds } 692c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str); 693c0d3fb39STakashi Iwai 694c309c467STakashi Iwai /* 6951da177e4SLinus Torvalds * snd_info_create_entry - create an info entry 6961da177e4SLinus Torvalds * @name: the proc file name 6978e7ccb7bSTakashi Iwai * @parent: the parent directory 6981da177e4SLinus Torvalds * 6991da177e4SLinus Torvalds * Creates an info entry with the given file name and initializes as 7001da177e4SLinus Torvalds * the default state. 7011da177e4SLinus Torvalds * 7021da177e4SLinus Torvalds * Usually called from other functions such as 7031da177e4SLinus Torvalds * snd_info_create_card_entry(). 7041da177e4SLinus Torvalds * 705eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 7061da177e4SLinus Torvalds */ 7078e7ccb7bSTakashi Iwai static struct snd_info_entry * 7088e7ccb7bSTakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent) 7091da177e4SLinus Torvalds { 71024c1f931STakashi Iwai struct snd_info_entry *entry; 711ca2c0966STakashi Iwai entry = kzalloc(sizeof(*entry), GFP_KERNEL); 7121da177e4SLinus Torvalds if (entry == NULL) 7131da177e4SLinus Torvalds return NULL; 714543537bdSPaulo Marques entry->name = kstrdup(name, GFP_KERNEL); 7151da177e4SLinus Torvalds if (entry->name == NULL) { 7161da177e4SLinus Torvalds kfree(entry); 7171da177e4SLinus Torvalds return NULL; 7181da177e4SLinus Torvalds } 7196a73cf46SJoe Perches entry->mode = S_IFREG | 0444; 7201da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_TEXT; 7211a60d4c5SIngo Molnar mutex_init(&entry->access); 722746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->children); 723746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->list); 7248e7ccb7bSTakashi Iwai entry->parent = parent; 7258e7ccb7bSTakashi Iwai if (parent) 7268e7ccb7bSTakashi Iwai list_add_tail(&entry->list, &parent->children); 7271da177e4SLinus Torvalds return entry; 7281da177e4SLinus Torvalds } 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds /** 7311da177e4SLinus Torvalds * snd_info_create_module_entry - create an info entry for the given module 7321da177e4SLinus Torvalds * @module: the module pointer 7331da177e4SLinus Torvalds * @name: the file name 7341da177e4SLinus Torvalds * @parent: the parent directory 7351da177e4SLinus Torvalds * 7361da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given module. 7371da177e4SLinus Torvalds * 738eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 7391da177e4SLinus Torvalds */ 74024c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module, 7411da177e4SLinus Torvalds const char *name, 74224c1f931STakashi Iwai struct snd_info_entry *parent) 7431da177e4SLinus Torvalds { 7443a554371STakashi Iwai struct snd_info_entry *entry; 7453a554371STakashi Iwai 7463a554371STakashi Iwai if (!parent) 7473a554371STakashi Iwai parent = snd_proc_root; 7483a554371STakashi Iwai entry = snd_info_create_entry(name, parent); 7498e7ccb7bSTakashi Iwai if (entry) 7501da177e4SLinus Torvalds entry->module = module; 7511da177e4SLinus Torvalds return entry; 7521da177e4SLinus Torvalds } 753c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry); 754c0d3fb39STakashi Iwai 7551da177e4SLinus Torvalds /** 7561da177e4SLinus Torvalds * snd_info_create_card_entry - create an info entry for the given card 7571da177e4SLinus Torvalds * @card: the card instance 7581da177e4SLinus Torvalds * @name: the file name 7591da177e4SLinus Torvalds * @parent: the parent directory 7601da177e4SLinus Torvalds * 7611da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card. 7621da177e4SLinus Torvalds * 763eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 7641da177e4SLinus Torvalds */ 76524c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, 7661da177e4SLinus Torvalds const char *name, 76724c1f931STakashi Iwai struct snd_info_entry * parent) 7681da177e4SLinus Torvalds { 7693a554371STakashi Iwai struct snd_info_entry *entry; 7703a554371STakashi Iwai 7713a554371STakashi Iwai if (!parent) 7723a554371STakashi Iwai parent = card->proc_root; 7733a554371STakashi Iwai entry = snd_info_create_entry(name, parent); 7741da177e4SLinus Torvalds if (entry) { 7751da177e4SLinus Torvalds entry->module = card->module; 7761da177e4SLinus Torvalds entry->card = card; 7771da177e4SLinus Torvalds } 7781da177e4SLinus Torvalds return entry; 7791da177e4SLinus Torvalds } 780c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry); 781c0d3fb39STakashi Iwai 782746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry) 783746d4a02STakashi Iwai { 78490a409aaSTakashi Iwai struct snd_info_entry *p; 785746d4a02STakashi Iwai 786746d4a02STakashi Iwai if (!entry->p) 787746d4a02STakashi Iwai return; 78890a409aaSTakashi Iwai list_for_each_entry(p, &entry->children, list) 789c560a679STakashi Iwai snd_info_disconnect(p); 790a8ca16eaSDavid Howells proc_remove(entry->p); 791746d4a02STakashi Iwai entry->p = NULL; 792746d4a02STakashi Iwai } 793746d4a02STakashi Iwai 7941da177e4SLinus Torvalds /** 7951da177e4SLinus Torvalds * snd_info_free_entry - release the info entry 7961da177e4SLinus Torvalds * @entry: the info entry 7971da177e4SLinus Torvalds * 798c560a679STakashi Iwai * Releases the info entry. 7991da177e4SLinus Torvalds */ 80024c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry) 8011da177e4SLinus Torvalds { 802c560a679STakashi Iwai struct snd_info_entry *p, *n; 803c560a679STakashi Iwai 804c560a679STakashi Iwai if (!entry) 8051da177e4SLinus Torvalds return; 806746d4a02STakashi Iwai if (entry->p) { 807746d4a02STakashi Iwai mutex_lock(&info_mutex); 808746d4a02STakashi Iwai snd_info_disconnect(entry); 809746d4a02STakashi Iwai mutex_unlock(&info_mutex); 810746d4a02STakashi Iwai } 811c560a679STakashi Iwai 812c560a679STakashi Iwai /* free all children at first */ 813c560a679STakashi Iwai list_for_each_entry_safe(p, n, &entry->children, list) 814c560a679STakashi Iwai snd_info_free_entry(p); 815c560a679STakashi Iwai 81690a409aaSTakashi Iwai list_del(&entry->list); 8171da177e4SLinus Torvalds kfree(entry->name); 8181da177e4SLinus Torvalds if (entry->private_free) 8191da177e4SLinus Torvalds entry->private_free(entry); 8201da177e4SLinus Torvalds kfree(entry); 8211da177e4SLinus Torvalds } 822c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry); 823c0d3fb39STakashi Iwai 8241da177e4SLinus Torvalds /** 8251da177e4SLinus Torvalds * snd_info_register - register the info entry 8261da177e4SLinus Torvalds * @entry: the info entry 8271da177e4SLinus Torvalds * 8281da177e4SLinus Torvalds * Registers the proc info entry. 8291da177e4SLinus Torvalds * 830eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 8311da177e4SLinus Torvalds */ 83224c1f931STakashi Iwai int snd_info_register(struct snd_info_entry * entry) 8331da177e4SLinus Torvalds { 8341da177e4SLinus Torvalds struct proc_dir_entry *root, *p = NULL; 8351da177e4SLinus Torvalds 8367eaa943cSTakashi Iwai if (snd_BUG_ON(!entry)) 8377eaa943cSTakashi Iwai return -ENXIO; 838644dbd64STakashi Iwai root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; 8391a60d4c5SIngo Molnar mutex_lock(&info_mutex); 840aee0c612SAl Viro if (S_ISDIR(entry->mode)) { 841aee0c612SAl Viro p = proc_mkdir_mode(entry->name, entry->mode, root); 8421da177e4SLinus Torvalds if (!p) { 8431a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 8441da177e4SLinus Torvalds return -ENOMEM; 8451da177e4SLinus Torvalds } 846aee0c612SAl Viro } else { 8474adb7bcbSTakashi Iwai const struct file_operations *ops; 8484adb7bcbSTakashi Iwai if (entry->content == SNDRV_INFO_CONTENT_DATA) 8494adb7bcbSTakashi Iwai ops = &snd_info_entry_operations; 8504adb7bcbSTakashi Iwai else 8514adb7bcbSTakashi Iwai ops = &snd_info_text_entry_ops; 852aee0c612SAl Viro p = proc_create_data(entry->name, entry->mode, root, 8534adb7bcbSTakashi Iwai ops, entry); 854aee0c612SAl Viro if (!p) { 855aee0c612SAl Viro mutex_unlock(&info_mutex); 856aee0c612SAl Viro return -ENOMEM; 857aee0c612SAl Viro } 858271a15eaSDavid Howells proc_set_size(p, entry->size); 859aee0c612SAl Viro } 8601da177e4SLinus Torvalds entry->p = p; 8611a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 8621da177e4SLinus Torvalds return 0; 8631da177e4SLinus Torvalds } 864c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register); 865c0d3fb39STakashi Iwai 8661da177e4SLinus Torvalds /* 8671da177e4SLinus Torvalds 8681da177e4SLinus Torvalds */ 8691da177e4SLinus Torvalds 87024c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 8711da177e4SLinus Torvalds { 8721da177e4SLinus Torvalds snd_iprintf(buffer, 87342662748SJaroslav Kysela "Advanced Linux Sound Architecture Driver Version k%s.\n", 87442662748SJaroslav Kysela init_utsname()->release); 8751da177e4SLinus Torvalds } 8761da177e4SLinus Torvalds 8771da177e4SLinus Torvalds static int __init snd_info_version_init(void) 8781da177e4SLinus Torvalds { 87924c1f931STakashi Iwai struct snd_info_entry *entry; 8801da177e4SLinus Torvalds 8811da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); 8821da177e4SLinus Torvalds if (entry == NULL) 8831da177e4SLinus Torvalds return -ENOMEM; 8841da177e4SLinus Torvalds entry->c.text.read = snd_info_version_read; 885b591b6e9STakashi Iwai return snd_info_register(entry); /* freed in error path */ 8861da177e4SLinus Torvalds } 887