11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Information interface for ALSA driver 4c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds #include <linux/init.h> 81da177e4SLinus Torvalds #include <linux/time.h> 927ac792cSAndrea Righi #include <linux/mm.h> 105a0e3ad6STejun Heo #include <linux/slab.h> 11543537bdSPaulo Marques #include <linux/string.h> 12da155d5bSPaul Gortmaker #include <linux/module.h> 131da177e4SLinus Torvalds #include <sound/core.h> 141da177e4SLinus Torvalds #include <sound/minors.h> 151da177e4SLinus Torvalds #include <sound/info.h> 1642662748SJaroslav Kysela #include <linux/utsname.h> 171da177e4SLinus Torvalds #include <linux/proc_fs.h> 181a60d4c5SIngo Molnar #include <linux/mutex.h> 191da177e4SLinus Torvalds #include <stdarg.h> 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds int snd_info_check_reserved_words(const char *str) 221da177e4SLinus Torvalds { 231da177e4SLinus Torvalds static char *reserved[] = 241da177e4SLinus Torvalds { 251da177e4SLinus Torvalds "version", 261da177e4SLinus Torvalds "meminfo", 271da177e4SLinus Torvalds "memdebug", 281da177e4SLinus Torvalds "detect", 291da177e4SLinus Torvalds "devices", 301da177e4SLinus Torvalds "oss", 311da177e4SLinus Torvalds "cards", 321da177e4SLinus Torvalds "timers", 331da177e4SLinus Torvalds "synth", 341da177e4SLinus Torvalds "pcm", 351da177e4SLinus Torvalds "seq", 361da177e4SLinus Torvalds NULL 371da177e4SLinus Torvalds }; 381da177e4SLinus Torvalds char **xstr = reserved; 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds while (*xstr) { 411da177e4SLinus Torvalds if (!strcmp(*xstr, str)) 421da177e4SLinus Torvalds return 0; 431da177e4SLinus Torvalds xstr++; 441da177e4SLinus Torvalds } 451da177e4SLinus Torvalds if (!strncmp(str, "card", 4)) 461da177e4SLinus Torvalds return 0; 471da177e4SLinus Torvalds return 1; 481da177e4SLinus Torvalds } 491da177e4SLinus Torvalds 501a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex); 511da177e4SLinus Torvalds 5224c1f931STakashi Iwai struct snd_info_private_data { 5324c1f931STakashi Iwai struct snd_info_buffer *rbuffer; 5424c1f931STakashi Iwai struct snd_info_buffer *wbuffer; 5524c1f931STakashi Iwai struct snd_info_entry *entry; 561da177e4SLinus Torvalds void *file_private_data; 5724c1f931STakashi Iwai }; 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds static int snd_info_version_init(void); 60746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry); 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds /* 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds */ 651da177e4SLinus Torvalds 66644dbd64STakashi Iwai static struct snd_info_entry *snd_proc_root; 676581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root; 68c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root); 69c0d3fb39STakashi Iwai 701da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 716581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root; 721da177e4SLinus Torvalds #endif 731da177e4SLinus Torvalds 744adb7bcbSTakashi Iwai static int alloc_info_private(struct snd_info_entry *entry, 754adb7bcbSTakashi Iwai struct snd_info_private_data **ret) 764adb7bcbSTakashi Iwai { 774adb7bcbSTakashi Iwai struct snd_info_private_data *data; 784adb7bcbSTakashi Iwai 794adb7bcbSTakashi Iwai if (!entry || !entry->p) 804adb7bcbSTakashi Iwai return -ENODEV; 814adb7bcbSTakashi Iwai if (!try_module_get(entry->module)) 824adb7bcbSTakashi Iwai return -EFAULT; 834adb7bcbSTakashi Iwai data = kzalloc(sizeof(*data), GFP_KERNEL); 844adb7bcbSTakashi Iwai if (!data) { 854adb7bcbSTakashi Iwai module_put(entry->module); 864adb7bcbSTakashi Iwai return -ENOMEM; 874adb7bcbSTakashi Iwai } 884adb7bcbSTakashi Iwai data->entry = entry; 894adb7bcbSTakashi Iwai *ret = data; 904adb7bcbSTakashi Iwai return 0; 914adb7bcbSTakashi Iwai } 924adb7bcbSTakashi Iwai 934adb7bcbSTakashi Iwai static bool valid_pos(loff_t pos, size_t count) 944adb7bcbSTakashi Iwai { 954adb7bcbSTakashi Iwai if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 964adb7bcbSTakashi Iwai return false; 974adb7bcbSTakashi Iwai if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 984adb7bcbSTakashi Iwai return false; 994adb7bcbSTakashi Iwai return true; 1004adb7bcbSTakashi Iwai } 1014adb7bcbSTakashi Iwai 1024adb7bcbSTakashi Iwai /* 1034adb7bcbSTakashi Iwai * file ops for binary proc files 1044adb7bcbSTakashi Iwai */ 1051da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) 1061da177e4SLinus Torvalds { 10724c1f931STakashi Iwai struct snd_info_private_data *data; 1081da177e4SLinus Torvalds struct snd_info_entry *entry; 10973029e0fSTakashi Iwai loff_t ret = -EINVAL, size; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds data = file->private_data; 1121da177e4SLinus Torvalds entry = data->entry; 1135b5cd553STakashi Iwai mutex_lock(&entry->access); 1144adb7bcbSTakashi Iwai if (entry->c.ops->llseek) { 11573029e0fSTakashi Iwai offset = entry->c.ops->llseek(entry, 1161da177e4SLinus Torvalds data->file_private_data, 1171da177e4SLinus Torvalds file, offset, orig); 1181da177e4SLinus Torvalds goto out; 1191da177e4SLinus Torvalds } 1204adb7bcbSTakashi Iwai 12173029e0fSTakashi Iwai size = entry->size; 12273029e0fSTakashi Iwai switch (orig) { 12373029e0fSTakashi Iwai case SEEK_SET: 1241da177e4SLinus Torvalds break; 12573029e0fSTakashi Iwai case SEEK_CUR: 12673029e0fSTakashi Iwai offset += file->f_pos; 12773029e0fSTakashi Iwai break; 12873029e0fSTakashi Iwai case SEEK_END: 12973029e0fSTakashi Iwai if (!size) 13073029e0fSTakashi Iwai goto out; 13173029e0fSTakashi Iwai offset += size; 13273029e0fSTakashi Iwai break; 13373029e0fSTakashi Iwai default: 13473029e0fSTakashi Iwai goto out; 1351da177e4SLinus Torvalds } 13673029e0fSTakashi Iwai if (offset < 0) 13773029e0fSTakashi Iwai goto out; 13873029e0fSTakashi Iwai if (size && offset > size) 13973029e0fSTakashi Iwai offset = size; 14073029e0fSTakashi Iwai file->f_pos = offset; 14173029e0fSTakashi Iwai ret = offset; 1421da177e4SLinus Torvalds out: 1435b5cd553STakashi Iwai mutex_unlock(&entry->access); 1441da177e4SLinus Torvalds return ret; 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, 1481da177e4SLinus Torvalds size_t count, loff_t * offset) 1491da177e4SLinus Torvalds { 1504adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 1514adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 1524adb7bcbSTakashi Iwai size_t size; 1531da177e4SLinus Torvalds loff_t pos; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds pos = *offset; 1564adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 1571da177e4SLinus Torvalds return -EIO; 158d97e1b78STakashi Iwai if (pos >= entry->size) 159d97e1b78STakashi Iwai return 0; 160d97e1b78STakashi Iwai size = entry->size - pos; 161d97e1b78STakashi Iwai size = min(count, size); 1624adb7bcbSTakashi Iwai size = entry->c.ops->read(entry, data->file_private_data, 163d97e1b78STakashi Iwai file, buffer, size, pos); 1641da177e4SLinus Torvalds if ((ssize_t) size > 0) 1651da177e4SLinus Torvalds *offset = pos + size; 1661da177e4SLinus Torvalds return size; 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const 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; 1747e4eeec8STakashi Iwai ssize_t size = 0; 1751da177e4SLinus Torvalds loff_t pos; 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds pos = *offset; 1784adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 1791da177e4SLinus Torvalds return -EIO; 1804adb7bcbSTakashi Iwai if (count > 0) { 181d97e1b78STakashi Iwai size_t maxsize = entry->size - pos; 182d97e1b78STakashi Iwai count = min(count, maxsize); 1834adb7bcbSTakashi Iwai size = entry->c.ops->write(entry, data->file_private_data, 1841da177e4SLinus Torvalds file, buffer, count, pos); 185d97e1b78STakashi Iwai } 1864adb7bcbSTakashi Iwai if (size > 0) 1871da177e4SLinus Torvalds *offset = pos + size; 1881da177e4SLinus Torvalds return size; 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds 191680ef72aSAl Viro static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait) 1921da177e4SLinus Torvalds { 1934adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 1944adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 195680ef72aSAl Viro __poll_t mask = 0; 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds if (entry->c.ops->poll) 1981da177e4SLinus Torvalds return entry->c.ops->poll(entry, 1991da177e4SLinus Torvalds data->file_private_data, 2001da177e4SLinus Torvalds file, wait); 2011da177e4SLinus Torvalds if (entry->c.ops->read) 202a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM; 2031da177e4SLinus Torvalds if (entry->c.ops->write) 204a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM; 2051da177e4SLinus Torvalds return mask; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds 208d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, 209d99e9889SIngo Molnar unsigned long arg) 2101da177e4SLinus Torvalds { 2114adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2124adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 2131da177e4SLinus Torvalds 2144adb7bcbSTakashi Iwai if (!entry->c.ops->ioctl) 2151da177e4SLinus Torvalds return -ENOTTY; 2164adb7bcbSTakashi Iwai return entry->c.ops->ioctl(entry, data->file_private_data, 2174adb7bcbSTakashi Iwai file, cmd, arg); 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) 2211da177e4SLinus Torvalds { 222496ad9aaSAl Viro struct inode *inode = file_inode(file); 22324c1f931STakashi Iwai struct snd_info_private_data *data; 2241da177e4SLinus Torvalds struct snd_info_entry *entry; 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds data = file->private_data; 2271da177e4SLinus Torvalds if (data == NULL) 2281da177e4SLinus Torvalds return 0; 2291da177e4SLinus Torvalds entry = data->entry; 2304adb7bcbSTakashi Iwai if (!entry->c.ops->mmap) 2311da177e4SLinus Torvalds return -ENXIO; 2324adb7bcbSTakashi Iwai return entry->c.ops->mmap(entry, data->file_private_data, 2334adb7bcbSTakashi Iwai inode, file, vma); 2344adb7bcbSTakashi Iwai } 2354adb7bcbSTakashi Iwai 2364adb7bcbSTakashi Iwai static int snd_info_entry_open(struct inode *inode, struct file *file) 2374adb7bcbSTakashi Iwai { 2384adb7bcbSTakashi Iwai struct snd_info_entry *entry = PDE_DATA(inode); 2394adb7bcbSTakashi Iwai struct snd_info_private_data *data; 2404adb7bcbSTakashi Iwai int mode, err; 2414adb7bcbSTakashi Iwai 2424adb7bcbSTakashi Iwai mutex_lock(&info_mutex); 2434adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data); 2444adb7bcbSTakashi Iwai if (err < 0) 2454adb7bcbSTakashi Iwai goto unlock; 2464adb7bcbSTakashi Iwai 2474adb7bcbSTakashi Iwai mode = file->f_flags & O_ACCMODE; 2484adb7bcbSTakashi Iwai if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) || 2494adb7bcbSTakashi Iwai ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) { 2504adb7bcbSTakashi Iwai err = -ENODEV; 2514adb7bcbSTakashi Iwai goto error; 2524adb7bcbSTakashi Iwai } 2534adb7bcbSTakashi Iwai 2544adb7bcbSTakashi Iwai if (entry->c.ops->open) { 2554adb7bcbSTakashi Iwai err = entry->c.ops->open(entry, mode, &data->file_private_data); 2564adb7bcbSTakashi Iwai if (err < 0) 2574adb7bcbSTakashi Iwai goto error; 2584adb7bcbSTakashi Iwai } 2594adb7bcbSTakashi Iwai 2604adb7bcbSTakashi Iwai file->private_data = data; 2614adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 2624adb7bcbSTakashi Iwai return 0; 2634adb7bcbSTakashi Iwai 2644adb7bcbSTakashi Iwai error: 2654adb7bcbSTakashi Iwai kfree(data); 2664adb7bcbSTakashi Iwai module_put(entry->module); 2674adb7bcbSTakashi Iwai unlock: 2684adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 2694adb7bcbSTakashi Iwai return err; 2704adb7bcbSTakashi Iwai } 2714adb7bcbSTakashi Iwai 2724adb7bcbSTakashi Iwai static int snd_info_entry_release(struct inode *inode, struct file *file) 2734adb7bcbSTakashi Iwai { 2744adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2754adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 2764adb7bcbSTakashi Iwai 2774adb7bcbSTakashi Iwai if (entry->c.ops->release) 2784adb7bcbSTakashi Iwai entry->c.ops->release(entry, file->f_flags & O_ACCMODE, 2794adb7bcbSTakashi Iwai data->file_private_data); 2804adb7bcbSTakashi Iwai module_put(entry->module); 2814adb7bcbSTakashi Iwai kfree(data); 2824adb7bcbSTakashi Iwai return 0; 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds 2859c2e08c5SArjan van de Ven static const struct file_operations snd_info_entry_operations = 2861da177e4SLinus Torvalds { 2871da177e4SLinus Torvalds .owner = THIS_MODULE, 2881da177e4SLinus Torvalds .llseek = snd_info_entry_llseek, 2891da177e4SLinus Torvalds .read = snd_info_entry_read, 2901da177e4SLinus Torvalds .write = snd_info_entry_write, 2911da177e4SLinus Torvalds .poll = snd_info_entry_poll, 292d99e9889SIngo Molnar .unlocked_ioctl = snd_info_entry_ioctl, 2931da177e4SLinus Torvalds .mmap = snd_info_entry_mmap, 2941da177e4SLinus Torvalds .open = snd_info_entry_open, 2951da177e4SLinus Torvalds .release = snd_info_entry_release, 2961da177e4SLinus Torvalds }; 2971da177e4SLinus Torvalds 2984adb7bcbSTakashi Iwai /* 2994adb7bcbSTakashi Iwai * file ops for text proc files 3004adb7bcbSTakashi Iwai */ 3014adb7bcbSTakashi Iwai static ssize_t snd_info_text_entry_write(struct file *file, 3024adb7bcbSTakashi Iwai const char __user *buffer, 3034adb7bcbSTakashi Iwai size_t count, loff_t *offset) 3044adb7bcbSTakashi Iwai { 3054adb7bcbSTakashi Iwai struct seq_file *m = file->private_data; 3064adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private; 3074adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 3084adb7bcbSTakashi Iwai struct snd_info_buffer *buf; 3094adb7bcbSTakashi Iwai loff_t pos; 3104adb7bcbSTakashi Iwai size_t next; 3114adb7bcbSTakashi Iwai int err = 0; 3124adb7bcbSTakashi Iwai 3136809cd68STakashi Iwai if (!entry->c.text.write) 3146809cd68STakashi Iwai return -EIO; 3154adb7bcbSTakashi Iwai pos = *offset; 3164adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 3174adb7bcbSTakashi Iwai return -EIO; 3184adb7bcbSTakashi Iwai next = pos + count; 319027a9fe6STakashi Iwai /* don't handle too large text inputs */ 320027a9fe6STakashi Iwai if (next > 16 * 1024) 321027a9fe6STakashi Iwai return -EIO; 3224adb7bcbSTakashi Iwai mutex_lock(&entry->access); 3234adb7bcbSTakashi Iwai buf = data->wbuffer; 3244adb7bcbSTakashi Iwai if (!buf) { 3254adb7bcbSTakashi Iwai data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3264adb7bcbSTakashi Iwai if (!buf) { 3274adb7bcbSTakashi Iwai err = -ENOMEM; 3284adb7bcbSTakashi Iwai goto error; 3294adb7bcbSTakashi Iwai } 3304adb7bcbSTakashi Iwai } 3314adb7bcbSTakashi Iwai if (next > buf->len) { 332ffb73b08STakashi Iwai char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL); 3334adb7bcbSTakashi Iwai if (!nbuf) { 3344adb7bcbSTakashi Iwai err = -ENOMEM; 3354adb7bcbSTakashi Iwai goto error; 3364adb7bcbSTakashi Iwai } 337ffb73b08STakashi Iwai kvfree(buf->buffer); 3384adb7bcbSTakashi Iwai buf->buffer = nbuf; 3394adb7bcbSTakashi Iwai buf->len = PAGE_ALIGN(next); 3404adb7bcbSTakashi Iwai } 3414adb7bcbSTakashi Iwai if (copy_from_user(buf->buffer + pos, buffer, count)) { 3424adb7bcbSTakashi Iwai err = -EFAULT; 3434adb7bcbSTakashi Iwai goto error; 3444adb7bcbSTakashi Iwai } 3454adb7bcbSTakashi Iwai buf->size = next; 3464adb7bcbSTakashi Iwai error: 3474adb7bcbSTakashi Iwai mutex_unlock(&entry->access); 3484adb7bcbSTakashi Iwai if (err < 0) 3494adb7bcbSTakashi Iwai return err; 3504adb7bcbSTakashi Iwai *offset = next; 3514adb7bcbSTakashi Iwai return count; 3524adb7bcbSTakashi Iwai } 3534adb7bcbSTakashi Iwai 3544adb7bcbSTakashi Iwai static int snd_info_seq_show(struct seq_file *seq, void *p) 3554adb7bcbSTakashi Iwai { 3564adb7bcbSTakashi Iwai struct snd_info_private_data *data = seq->private; 3574adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 3584adb7bcbSTakashi Iwai 3596809cd68STakashi Iwai if (!entry->c.text.read) { 3606809cd68STakashi Iwai return -EIO; 3616809cd68STakashi Iwai } else { 3624adb7bcbSTakashi Iwai data->rbuffer->buffer = (char *)seq; /* XXX hack! */ 3634adb7bcbSTakashi Iwai entry->c.text.read(entry, data->rbuffer); 3644adb7bcbSTakashi Iwai } 3654adb7bcbSTakashi Iwai return 0; 3664adb7bcbSTakashi Iwai } 3674adb7bcbSTakashi Iwai 3684adb7bcbSTakashi Iwai static int snd_info_text_entry_open(struct inode *inode, struct file *file) 3694adb7bcbSTakashi Iwai { 3704adb7bcbSTakashi Iwai struct snd_info_entry *entry = PDE_DATA(inode); 3714adb7bcbSTakashi Iwai struct snd_info_private_data *data; 3724adb7bcbSTakashi Iwai int err; 3734adb7bcbSTakashi Iwai 3744adb7bcbSTakashi Iwai mutex_lock(&info_mutex); 3754adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data); 3764adb7bcbSTakashi Iwai if (err < 0) 3774adb7bcbSTakashi Iwai goto unlock; 3784adb7bcbSTakashi Iwai 3794adb7bcbSTakashi Iwai data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL); 3804adb7bcbSTakashi Iwai if (!data->rbuffer) { 3814adb7bcbSTakashi Iwai err = -ENOMEM; 3824adb7bcbSTakashi Iwai goto error; 3834adb7bcbSTakashi Iwai } 3844adb7bcbSTakashi Iwai if (entry->size) 3854adb7bcbSTakashi Iwai err = single_open_size(file, snd_info_seq_show, data, 3864adb7bcbSTakashi Iwai entry->size); 3874adb7bcbSTakashi Iwai else 3884adb7bcbSTakashi Iwai err = single_open(file, snd_info_seq_show, data); 3894adb7bcbSTakashi Iwai if (err < 0) 3904adb7bcbSTakashi Iwai goto error; 3914adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 3924adb7bcbSTakashi Iwai return 0; 3934adb7bcbSTakashi Iwai 3944adb7bcbSTakashi Iwai error: 3954adb7bcbSTakashi Iwai kfree(data->rbuffer); 3964adb7bcbSTakashi Iwai kfree(data); 3974adb7bcbSTakashi Iwai module_put(entry->module); 3984adb7bcbSTakashi Iwai unlock: 3994adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 4004adb7bcbSTakashi Iwai return err; 4014adb7bcbSTakashi Iwai } 4024adb7bcbSTakashi Iwai 4034adb7bcbSTakashi Iwai static int snd_info_text_entry_release(struct inode *inode, struct file *file) 4044adb7bcbSTakashi Iwai { 4054adb7bcbSTakashi Iwai struct seq_file *m = file->private_data; 4064adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private; 4074adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 4084adb7bcbSTakashi Iwai 4094adb7bcbSTakashi Iwai if (data->wbuffer && entry->c.text.write) 4104adb7bcbSTakashi Iwai entry->c.text.write(entry, data->wbuffer); 4114adb7bcbSTakashi Iwai 4124adb7bcbSTakashi Iwai single_release(inode, file); 4134adb7bcbSTakashi Iwai kfree(data->rbuffer); 4144adb7bcbSTakashi Iwai if (data->wbuffer) { 415ffb73b08STakashi Iwai kvfree(data->wbuffer->buffer); 4164adb7bcbSTakashi Iwai kfree(data->wbuffer); 4174adb7bcbSTakashi Iwai } 4184adb7bcbSTakashi Iwai 4194adb7bcbSTakashi Iwai module_put(entry->module); 4204adb7bcbSTakashi Iwai kfree(data); 4214adb7bcbSTakashi Iwai return 0; 4224adb7bcbSTakashi Iwai } 4234adb7bcbSTakashi Iwai 4244adb7bcbSTakashi Iwai static const struct file_operations snd_info_text_entry_ops = 4254adb7bcbSTakashi Iwai { 4264adb7bcbSTakashi Iwai .owner = THIS_MODULE, 4274adb7bcbSTakashi Iwai .open = snd_info_text_entry_open, 4284adb7bcbSTakashi Iwai .release = snd_info_text_entry_release, 4294adb7bcbSTakashi Iwai .write = snd_info_text_entry_write, 4304adb7bcbSTakashi Iwai .llseek = seq_lseek, 4314adb7bcbSTakashi Iwai .read = seq_read, 4324adb7bcbSTakashi Iwai }; 4334adb7bcbSTakashi Iwai 434886364f6STakashi Iwai static struct snd_info_entry *create_subdir(struct module *mod, 435886364f6STakashi Iwai const char *name) 436886364f6STakashi Iwai { 437886364f6STakashi Iwai struct snd_info_entry *entry; 438886364f6STakashi Iwai 439886364f6STakashi Iwai entry = snd_info_create_module_entry(mod, name, NULL); 440886364f6STakashi Iwai if (!entry) 441886364f6STakashi Iwai return NULL; 4426a73cf46SJoe Perches entry->mode = S_IFDIR | 0555; 443886364f6STakashi Iwai if (snd_info_register(entry) < 0) { 444886364f6STakashi Iwai snd_info_free_entry(entry); 445886364f6STakashi Iwai return NULL; 446886364f6STakashi Iwai } 447886364f6STakashi Iwai return entry; 448886364f6STakashi Iwai } 449886364f6STakashi Iwai 4508e7ccb7bSTakashi Iwai static struct snd_info_entry * 451a858ee66STakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent, 452a858ee66STakashi Iwai struct module *module); 453644dbd64STakashi Iwai 4541da177e4SLinus Torvalds int __init snd_info_init(void) 4551da177e4SLinus Torvalds { 456a858ee66STakashi Iwai snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE); 457644dbd64STakashi Iwai if (!snd_proc_root) 4581da177e4SLinus Torvalds return -ENOMEM; 4596a73cf46SJoe Perches snd_proc_root->mode = S_IFDIR | 0555; 460644dbd64STakashi Iwai snd_proc_root->p = proc_mkdir("asound", NULL); 461644dbd64STakashi Iwai if (!snd_proc_root->p) 462644dbd64STakashi Iwai goto error; 4631da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 464886364f6STakashi Iwai snd_oss_root = create_subdir(THIS_MODULE, "oss"); 465886364f6STakashi Iwai if (!snd_oss_root) 466886364f6STakashi Iwai goto error; 4671da177e4SLinus Torvalds #endif 4688eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER) 469886364f6STakashi Iwai snd_seq_root = create_subdir(THIS_MODULE, "seq"); 470886364f6STakashi Iwai if (!snd_seq_root) 471886364f6STakashi Iwai goto error; 4721da177e4SLinus Torvalds #endif 473b591b6e9STakashi Iwai if (snd_info_version_init() < 0 || 474b591b6e9STakashi Iwai snd_minor_info_init() < 0 || 475b591b6e9STakashi Iwai snd_minor_info_oss_init() < 0 || 476a0dca822STakashi Iwai snd_card_info_init() < 0 || 477a0dca822STakashi Iwai snd_info_minor_register() < 0) 478b591b6e9STakashi Iwai goto error; 4791da177e4SLinus Torvalds return 0; 480886364f6STakashi Iwai 481886364f6STakashi Iwai error: 482644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root); 483886364f6STakashi Iwai return -ENOMEM; 4841da177e4SLinus Torvalds } 4851da177e4SLinus Torvalds 4861da177e4SLinus Torvalds int __exit snd_info_done(void) 4871da177e4SLinus Torvalds { 488644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root); 4891da177e4SLinus Torvalds return 0; 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds 49229b2625fSTakashi Iwai static void snd_card_id_read(struct snd_info_entry *entry, 49329b2625fSTakashi Iwai struct snd_info_buffer *buffer) 49429b2625fSTakashi Iwai { 49529b2625fSTakashi Iwai struct snd_card *card = entry->private_data; 49629b2625fSTakashi Iwai 49729b2625fSTakashi Iwai snd_iprintf(buffer, "%s\n", card->id); 49829b2625fSTakashi Iwai } 49929b2625fSTakashi Iwai 5001da177e4SLinus Torvalds /* 5011da177e4SLinus Torvalds * create a card proc file 5021da177e4SLinus Torvalds * called from init.c 5031da177e4SLinus Torvalds */ 50424c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card) 5051da177e4SLinus Torvalds { 5061da177e4SLinus Torvalds char str[8]; 50724c1f931STakashi Iwai struct snd_info_entry *entry; 5081da177e4SLinus Torvalds 5097eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 5107eaa943cSTakashi Iwai return -ENXIO; 5111da177e4SLinus Torvalds 5121da177e4SLinus Torvalds sprintf(str, "card%i", card->number); 513886364f6STakashi Iwai entry = create_subdir(card->module, str); 514886364f6STakashi Iwai if (!entry) 5151da177e4SLinus Torvalds return -ENOMEM; 5161da177e4SLinus Torvalds card->proc_root = entry; 51729b2625fSTakashi Iwai 51829b2625fSTakashi Iwai return snd_card_ro_proc_new(card, "id", card, snd_card_id_read); 5191da177e4SLinus Torvalds } 5201da177e4SLinus Torvalds 5211da177e4SLinus Torvalds /* 5221da177e4SLinus Torvalds * register the card proc file 5231da177e4SLinus Torvalds * called from init.c 5242471b6c8STakashi Iwai * can be called multiple times for reinitialization 5251da177e4SLinus Torvalds */ 52624c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card) 5271da177e4SLinus Torvalds { 5281da177e4SLinus Torvalds struct proc_dir_entry *p; 5292471b6c8STakashi Iwai int err; 5301da177e4SLinus Torvalds 5317eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 5327eaa943cSTakashi Iwai return -ENXIO; 5331da177e4SLinus Torvalds 534348c5ad5STakashi Iwai err = snd_info_register(card->proc_root); 5352471b6c8STakashi Iwai if (err < 0) 5362471b6c8STakashi Iwai return err; 5372471b6c8STakashi Iwai 5381da177e4SLinus Torvalds if (!strcmp(card->id, card->proc_root->name)) 5391da177e4SLinus Torvalds return 0; 5401da177e4SLinus Torvalds 5412471b6c8STakashi Iwai if (card->proc_root_link) 5422471b6c8STakashi Iwai return 0; 543644dbd64STakashi Iwai p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name); 5442471b6c8STakashi Iwai if (!p) 5451da177e4SLinus Torvalds return -ENOMEM; 5461da177e4SLinus Torvalds card->proc_root_link = p; 5471da177e4SLinus Torvalds return 0; 5481da177e4SLinus Torvalds } 5491da177e4SLinus Torvalds 5501da177e4SLinus Torvalds /* 551c2eb9c4eSJaroslav Kysela * called on card->id change 552c2eb9c4eSJaroslav Kysela */ 553c2eb9c4eSJaroslav Kysela void snd_info_card_id_change(struct snd_card *card) 554c2eb9c4eSJaroslav Kysela { 555c2eb9c4eSJaroslav Kysela mutex_lock(&info_mutex); 556c2eb9c4eSJaroslav Kysela if (card->proc_root_link) { 557a8ca16eaSDavid Howells proc_remove(card->proc_root_link); 558c2eb9c4eSJaroslav Kysela card->proc_root_link = NULL; 559c2eb9c4eSJaroslav Kysela } 560c2eb9c4eSJaroslav Kysela if (strcmp(card->id, card->proc_root->name)) 561c2eb9c4eSJaroslav Kysela card->proc_root_link = proc_symlink(card->id, 562644dbd64STakashi Iwai snd_proc_root->p, 563c2eb9c4eSJaroslav Kysela card->proc_root->name); 564c2eb9c4eSJaroslav Kysela mutex_unlock(&info_mutex); 565c2eb9c4eSJaroslav Kysela } 566c2eb9c4eSJaroslav Kysela 567c2eb9c4eSJaroslav Kysela /* 5681da177e4SLinus Torvalds * de-register the card proc file 5691da177e4SLinus Torvalds * called from init.c 5701da177e4SLinus Torvalds */ 571746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card) 5721da177e4SLinus Torvalds { 5737eaa943cSTakashi Iwai if (!card) 5747eaa943cSTakashi Iwai return; 575746d4a02STakashi Iwai mutex_lock(&info_mutex); 576a8ca16eaSDavid Howells proc_remove(card->proc_root_link); 5771da177e4SLinus Torvalds card->proc_root_link = NULL; 578746d4a02STakashi Iwai if (card->proc_root) 579746d4a02STakashi Iwai snd_info_disconnect(card->proc_root); 580746d4a02STakashi Iwai mutex_unlock(&info_mutex); 5811da177e4SLinus Torvalds } 582746d4a02STakashi Iwai 583746d4a02STakashi Iwai /* 584746d4a02STakashi Iwai * release the card proc file resources 585746d4a02STakashi Iwai * called from init.c 586746d4a02STakashi Iwai */ 587746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card) 588746d4a02STakashi Iwai { 5897eaa943cSTakashi Iwai if (!card) 5907eaa943cSTakashi Iwai return 0; 591746d4a02STakashi Iwai snd_info_free_entry(card->proc_root); 592746d4a02STakashi Iwai card->proc_root = NULL; 5931da177e4SLinus Torvalds return 0; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds 5961da177e4SLinus Torvalds 5971da177e4SLinus Torvalds /** 5981da177e4SLinus Torvalds * snd_info_get_line - read one line from the procfs buffer 5991da177e4SLinus Torvalds * @buffer: the procfs buffer 6001da177e4SLinus Torvalds * @line: the buffer to store 601ddc64b27SClemens Ladisch * @len: the max. buffer size 6021da177e4SLinus Torvalds * 6031da177e4SLinus Torvalds * Reads one line from the buffer and stores the string. 6041da177e4SLinus Torvalds * 605eb7c06e8SYacine Belkadi * Return: Zero if successful, or 1 if error or EOF. 6061da177e4SLinus Torvalds */ 60724c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) 6081da177e4SLinus Torvalds { 6091da177e4SLinus Torvalds int c = -1; 6101da177e4SLinus Torvalds 6110bc0ec90STakashi Iwai if (snd_BUG_ON(!buffer || !buffer->buffer)) 6120bc0ec90STakashi Iwai return 1; 6131da177e4SLinus Torvalds if (len <= 0 || buffer->stop || buffer->error) 6141da177e4SLinus Torvalds return 1; 6150bc0ec90STakashi Iwai while (!buffer->stop) { 6167e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++]; 6177e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) 6181da177e4SLinus Torvalds buffer->stop = 1; 6190bc0ec90STakashi Iwai if (c == '\n') 6201da177e4SLinus Torvalds break; 621ddc64b27SClemens Ladisch if (len > 1) { 6220bc0ec90STakashi Iwai len--; 6231da177e4SLinus Torvalds *line++ = c; 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds } 6261da177e4SLinus Torvalds *line = '\0'; 6271da177e4SLinus Torvalds return 0; 6281da177e4SLinus Torvalds } 629c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line); 630c0d3fb39STakashi Iwai 6311da177e4SLinus Torvalds /** 632856def8aSHenrik Kretzschmar * snd_info_get_str - parse a string token 6331da177e4SLinus Torvalds * @dest: the buffer to store the string token 6341da177e4SLinus Torvalds * @src: the original string 6351da177e4SLinus Torvalds * @len: the max. length of token - 1 6361da177e4SLinus Torvalds * 6371da177e4SLinus Torvalds * Parses the original string and copy a token to the given 6381da177e4SLinus Torvalds * string buffer. 6391da177e4SLinus Torvalds * 640eb7c06e8SYacine Belkadi * Return: The updated pointer of the original string so that 6411da177e4SLinus Torvalds * it can be used for the next call. 6421da177e4SLinus Torvalds */ 6434f7454a9STakashi Iwai const char *snd_info_get_str(char *dest, const char *src, int len) 6441da177e4SLinus Torvalds { 6451da177e4SLinus Torvalds int c; 6461da177e4SLinus Torvalds 6471da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 6481da177e4SLinus Torvalds src++; 6491da177e4SLinus Torvalds if (*src == '"' || *src == '\'') { 6501da177e4SLinus Torvalds c = *src++; 6511da177e4SLinus Torvalds while (--len > 0 && *src && *src != c) { 6521da177e4SLinus Torvalds *dest++ = *src++; 6531da177e4SLinus Torvalds } 6541da177e4SLinus Torvalds if (*src == c) 6551da177e4SLinus Torvalds src++; 6561da177e4SLinus Torvalds } else { 6571da177e4SLinus Torvalds while (--len > 0 && *src && *src != ' ' && *src != '\t') { 6581da177e4SLinus Torvalds *dest++ = *src++; 6591da177e4SLinus Torvalds } 6601da177e4SLinus Torvalds } 6611da177e4SLinus Torvalds *dest = 0; 6621da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 6631da177e4SLinus Torvalds src++; 6641da177e4SLinus Torvalds return src; 6651da177e4SLinus Torvalds } 666c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str); 667c0d3fb39STakashi Iwai 668c309c467STakashi Iwai /* 6691da177e4SLinus Torvalds * snd_info_create_entry - create an info entry 6701da177e4SLinus Torvalds * @name: the proc file name 6718e7ccb7bSTakashi Iwai * @parent: the parent directory 6721da177e4SLinus Torvalds * 6731da177e4SLinus Torvalds * Creates an info entry with the given file name and initializes as 6741da177e4SLinus Torvalds * the default state. 6751da177e4SLinus Torvalds * 6761da177e4SLinus Torvalds * Usually called from other functions such as 6771da177e4SLinus Torvalds * snd_info_create_card_entry(). 6781da177e4SLinus Torvalds * 679eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 6801da177e4SLinus Torvalds */ 6818e7ccb7bSTakashi Iwai static struct snd_info_entry * 682a858ee66STakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent, 683a858ee66STakashi Iwai struct module *module) 6841da177e4SLinus Torvalds { 68524c1f931STakashi Iwai struct snd_info_entry *entry; 686ca2c0966STakashi Iwai entry = kzalloc(sizeof(*entry), GFP_KERNEL); 6871da177e4SLinus Torvalds if (entry == NULL) 6881da177e4SLinus Torvalds return NULL; 689543537bdSPaulo Marques entry->name = kstrdup(name, GFP_KERNEL); 6901da177e4SLinus Torvalds if (entry->name == NULL) { 6911da177e4SLinus Torvalds kfree(entry); 6921da177e4SLinus Torvalds return NULL; 6931da177e4SLinus Torvalds } 6946a73cf46SJoe Perches entry->mode = S_IFREG | 0444; 6951da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_TEXT; 6961a60d4c5SIngo Molnar mutex_init(&entry->access); 697746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->children); 698746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->list); 6998e7ccb7bSTakashi Iwai entry->parent = parent; 700a858ee66STakashi Iwai entry->module = module; 7018c2f8708STakashi Iwai if (parent) { 7028c2f8708STakashi Iwai mutex_lock(&parent->access); 7038e7ccb7bSTakashi Iwai list_add_tail(&entry->list, &parent->children); 7048c2f8708STakashi Iwai mutex_unlock(&parent->access); 7058c2f8708STakashi Iwai } 7061da177e4SLinus Torvalds return entry; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds 7091da177e4SLinus Torvalds /** 7101da177e4SLinus Torvalds * snd_info_create_module_entry - create an info entry for the given module 7111da177e4SLinus Torvalds * @module: the module pointer 7121da177e4SLinus Torvalds * @name: the file name 7131da177e4SLinus Torvalds * @parent: the parent directory 7141da177e4SLinus Torvalds * 7151da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given module. 7161da177e4SLinus Torvalds * 717eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 7181da177e4SLinus Torvalds */ 71924c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module, 7201da177e4SLinus Torvalds const char *name, 72124c1f931STakashi Iwai struct snd_info_entry *parent) 7221da177e4SLinus Torvalds { 7233a554371STakashi Iwai if (!parent) 7243a554371STakashi Iwai parent = snd_proc_root; 725a858ee66STakashi Iwai return snd_info_create_entry(name, parent, module); 7261da177e4SLinus Torvalds } 727c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry); 728c0d3fb39STakashi Iwai 7291da177e4SLinus Torvalds /** 7301da177e4SLinus Torvalds * snd_info_create_card_entry - create an info entry for the given card 7311da177e4SLinus Torvalds * @card: the card instance 7321da177e4SLinus Torvalds * @name: the file name 7331da177e4SLinus Torvalds * @parent: the parent directory 7341da177e4SLinus Torvalds * 7351da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card. 7361da177e4SLinus Torvalds * 737eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 7381da177e4SLinus Torvalds */ 73924c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, 7401da177e4SLinus Torvalds const char *name, 74124c1f931STakashi Iwai struct snd_info_entry * parent) 7421da177e4SLinus Torvalds { 7433a554371STakashi Iwai if (!parent) 7443a554371STakashi Iwai parent = card->proc_root; 745a858ee66STakashi Iwai return snd_info_create_entry(name, parent, card->module); 7461da177e4SLinus Torvalds } 747c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry); 748c0d3fb39STakashi Iwai 749746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry) 750746d4a02STakashi Iwai { 75190a409aaSTakashi Iwai struct snd_info_entry *p; 752746d4a02STakashi Iwai 753746d4a02STakashi Iwai if (!entry->p) 754746d4a02STakashi Iwai return; 75590a409aaSTakashi Iwai list_for_each_entry(p, &entry->children, list) 756c560a679STakashi Iwai snd_info_disconnect(p); 757a8ca16eaSDavid Howells proc_remove(entry->p); 758746d4a02STakashi Iwai entry->p = NULL; 759746d4a02STakashi Iwai } 760746d4a02STakashi Iwai 7611da177e4SLinus Torvalds /** 7621da177e4SLinus Torvalds * snd_info_free_entry - release the info entry 7631da177e4SLinus Torvalds * @entry: the info entry 7641da177e4SLinus Torvalds * 765c560a679STakashi Iwai * Releases the info entry. 7661da177e4SLinus Torvalds */ 76724c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry) 7681da177e4SLinus Torvalds { 769c560a679STakashi Iwai struct snd_info_entry *p, *n; 770c560a679STakashi Iwai 771c560a679STakashi Iwai if (!entry) 7721da177e4SLinus Torvalds return; 773746d4a02STakashi Iwai if (entry->p) { 774746d4a02STakashi Iwai mutex_lock(&info_mutex); 775746d4a02STakashi Iwai snd_info_disconnect(entry); 776746d4a02STakashi Iwai mutex_unlock(&info_mutex); 777746d4a02STakashi Iwai } 778c560a679STakashi Iwai 779c560a679STakashi Iwai /* free all children at first */ 780c560a679STakashi Iwai list_for_each_entry_safe(p, n, &entry->children, list) 781c560a679STakashi Iwai snd_info_free_entry(p); 782c560a679STakashi Iwai 7838c2f8708STakashi Iwai p = entry->parent; 7848c2f8708STakashi Iwai if (p) { 7858c2f8708STakashi Iwai mutex_lock(&p->access); 78690a409aaSTakashi Iwai list_del(&entry->list); 7878c2f8708STakashi Iwai mutex_unlock(&p->access); 7888c2f8708STakashi Iwai } 7891da177e4SLinus Torvalds kfree(entry->name); 7901da177e4SLinus Torvalds if (entry->private_free) 7911da177e4SLinus Torvalds entry->private_free(entry); 7921da177e4SLinus Torvalds kfree(entry); 7931da177e4SLinus Torvalds } 794c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry); 795c0d3fb39STakashi Iwai 796348c5ad5STakashi Iwai static int __snd_info_register(struct snd_info_entry *entry) 7971da177e4SLinus Torvalds { 7981da177e4SLinus Torvalds struct proc_dir_entry *root, *p = NULL; 7991da177e4SLinus Torvalds 8007eaa943cSTakashi Iwai if (snd_BUG_ON(!entry)) 8017eaa943cSTakashi Iwai return -ENXIO; 802644dbd64STakashi Iwai root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; 8031a60d4c5SIngo Molnar mutex_lock(&info_mutex); 804348c5ad5STakashi Iwai if (entry->p || !root) 805348c5ad5STakashi Iwai goto unlock; 806aee0c612SAl Viro if (S_ISDIR(entry->mode)) { 807aee0c612SAl Viro p = proc_mkdir_mode(entry->name, entry->mode, root); 8081da177e4SLinus Torvalds if (!p) { 8091a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 8101da177e4SLinus Torvalds return -ENOMEM; 8111da177e4SLinus Torvalds } 812aee0c612SAl Viro } else { 8134adb7bcbSTakashi Iwai const struct file_operations *ops; 8144adb7bcbSTakashi Iwai if (entry->content == SNDRV_INFO_CONTENT_DATA) 8154adb7bcbSTakashi Iwai ops = &snd_info_entry_operations; 8164adb7bcbSTakashi Iwai else 8174adb7bcbSTakashi Iwai ops = &snd_info_text_entry_ops; 818aee0c612SAl Viro p = proc_create_data(entry->name, entry->mode, root, 8194adb7bcbSTakashi Iwai ops, entry); 820aee0c612SAl Viro if (!p) { 821aee0c612SAl Viro mutex_unlock(&info_mutex); 822aee0c612SAl Viro return -ENOMEM; 823aee0c612SAl Viro } 824271a15eaSDavid Howells proc_set_size(p, entry->size); 825aee0c612SAl Viro } 8261da177e4SLinus Torvalds entry->p = p; 827348c5ad5STakashi Iwai unlock: 8281a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 8291da177e4SLinus Torvalds return 0; 8301da177e4SLinus Torvalds } 831348c5ad5STakashi Iwai 832348c5ad5STakashi Iwai /** 833348c5ad5STakashi Iwai * snd_info_register - register the info entry 834348c5ad5STakashi Iwai * @entry: the info entry 835348c5ad5STakashi Iwai * 836348c5ad5STakashi Iwai * Registers the proc info entry. 837348c5ad5STakashi Iwai * The all children entries are registered recursively. 838348c5ad5STakashi Iwai * 839348c5ad5STakashi Iwai * Return: Zero if successful, or a negative error code on failure. 840348c5ad5STakashi Iwai */ 841348c5ad5STakashi Iwai int snd_info_register(struct snd_info_entry *entry) 842348c5ad5STakashi Iwai { 843348c5ad5STakashi Iwai struct snd_info_entry *p; 844348c5ad5STakashi Iwai int err; 845348c5ad5STakashi Iwai 846348c5ad5STakashi Iwai if (!entry->p) { 847348c5ad5STakashi Iwai err = __snd_info_register(entry); 848348c5ad5STakashi Iwai if (err < 0) 849348c5ad5STakashi Iwai return err; 850348c5ad5STakashi Iwai } 851348c5ad5STakashi Iwai 852348c5ad5STakashi Iwai list_for_each_entry(p, &entry->children, list) { 853348c5ad5STakashi Iwai err = snd_info_register(p); 854348c5ad5STakashi Iwai if (err < 0) 855348c5ad5STakashi Iwai return err; 856348c5ad5STakashi Iwai } 857348c5ad5STakashi Iwai 858348c5ad5STakashi Iwai return 0; 859348c5ad5STakashi Iwai } 860c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register); 861c0d3fb39STakashi Iwai 8627453e1daSTakashi Iwai /** 8637453e1daSTakashi Iwai * snd_card_rw_proc_new - Create a read/write text proc file entry for the card 8647453e1daSTakashi Iwai * @card: the card instance 8657453e1daSTakashi Iwai * @name: the file name 8667453e1daSTakashi Iwai * @private_data: the arbitrary private data 8677453e1daSTakashi Iwai * @read: the read callback 8687453e1daSTakashi Iwai * @write: the write callback, NULL for read-only 8697453e1daSTakashi Iwai * 8707453e1daSTakashi Iwai * This proc file entry will be registered via snd_card_register() call, and 8717453e1daSTakashi Iwai * it will be removed automatically at the card removal, too. 8727453e1daSTakashi Iwai */ 8737453e1daSTakashi Iwai int snd_card_rw_proc_new(struct snd_card *card, const char *name, 8747453e1daSTakashi Iwai void *private_data, 8757453e1daSTakashi Iwai void (*read)(struct snd_info_entry *, 8767453e1daSTakashi Iwai struct snd_info_buffer *), 8777453e1daSTakashi Iwai void (*write)(struct snd_info_entry *entry, 8787453e1daSTakashi Iwai struct snd_info_buffer *buffer)) 8797453e1daSTakashi Iwai { 8807453e1daSTakashi Iwai struct snd_info_entry *entry; 8817453e1daSTakashi Iwai 8827453e1daSTakashi Iwai entry = snd_info_create_card_entry(card, name, card->proc_root); 8837453e1daSTakashi Iwai if (!entry) 8847453e1daSTakashi Iwai return -ENOMEM; 8857453e1daSTakashi Iwai snd_info_set_text_ops(entry, private_data, read); 8867453e1daSTakashi Iwai if (write) { 8877453e1daSTakashi Iwai entry->mode |= 0200; 8887453e1daSTakashi Iwai entry->c.text.write = write; 8897453e1daSTakashi Iwai } 8907453e1daSTakashi Iwai return 0; 8917453e1daSTakashi Iwai } 8927453e1daSTakashi Iwai EXPORT_SYMBOL_GPL(snd_card_rw_proc_new); 8937453e1daSTakashi Iwai 8941da177e4SLinus Torvalds /* 8951da177e4SLinus Torvalds 8961da177e4SLinus Torvalds */ 8971da177e4SLinus Torvalds 89824c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 8991da177e4SLinus Torvalds { 9001da177e4SLinus Torvalds snd_iprintf(buffer, 90142662748SJaroslav Kysela "Advanced Linux Sound Architecture Driver Version k%s.\n", 90242662748SJaroslav Kysela init_utsname()->release); 9031da177e4SLinus Torvalds } 9041da177e4SLinus Torvalds 9051da177e4SLinus Torvalds static int __init snd_info_version_init(void) 9061da177e4SLinus Torvalds { 90724c1f931STakashi Iwai struct snd_info_entry *entry; 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); 9101da177e4SLinus Torvalds if (entry == NULL) 9111da177e4SLinus Torvalds return -ENOMEM; 9121da177e4SLinus Torvalds entry->c.text.read = snd_info_version_read; 913b591b6e9STakashi Iwai return snd_info_register(entry); /* freed in error path */ 9141da177e4SLinus Torvalds } 915