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 201da177e4SLinus Torvalds int snd_info_check_reserved_words(const char *str) 211da177e4SLinus Torvalds { 2251d7847aSTakashi Iwai static const char * const reserved[] = 231da177e4SLinus Torvalds { 241da177e4SLinus Torvalds "version", 251da177e4SLinus Torvalds "meminfo", 261da177e4SLinus Torvalds "memdebug", 271da177e4SLinus Torvalds "detect", 281da177e4SLinus Torvalds "devices", 291da177e4SLinus Torvalds "oss", 301da177e4SLinus Torvalds "cards", 311da177e4SLinus Torvalds "timers", 321da177e4SLinus Torvalds "synth", 331da177e4SLinus Torvalds "pcm", 341da177e4SLinus Torvalds "seq", 351da177e4SLinus Torvalds NULL 361da177e4SLinus Torvalds }; 3751d7847aSTakashi Iwai const char * const *xstr = reserved; 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds while (*xstr) { 401da177e4SLinus Torvalds if (!strcmp(*xstr, str)) 411da177e4SLinus Torvalds return 0; 421da177e4SLinus Torvalds xstr++; 431da177e4SLinus Torvalds } 441da177e4SLinus Torvalds if (!strncmp(str, "card", 4)) 451da177e4SLinus Torvalds return 0; 461da177e4SLinus Torvalds return 1; 471da177e4SLinus Torvalds } 481da177e4SLinus Torvalds 491a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex); 501da177e4SLinus Torvalds 5124c1f931STakashi Iwai struct snd_info_private_data { 5224c1f931STakashi Iwai struct snd_info_buffer *rbuffer; 5324c1f931STakashi Iwai struct snd_info_buffer *wbuffer; 5424c1f931STakashi Iwai struct snd_info_entry *entry; 551da177e4SLinus Torvalds void *file_private_data; 5624c1f931STakashi Iwai }; 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds static int snd_info_version_init(void); 59746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry); 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds */ 641da177e4SLinus Torvalds 65644dbd64STakashi Iwai static struct snd_info_entry *snd_proc_root; 666581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root; 67c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root); 68c0d3fb39STakashi Iwai 691da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 706581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root; 711da177e4SLinus Torvalds #endif 721da177e4SLinus Torvalds 734adb7bcbSTakashi Iwai static int alloc_info_private(struct snd_info_entry *entry, 744adb7bcbSTakashi Iwai struct snd_info_private_data **ret) 754adb7bcbSTakashi Iwai { 764adb7bcbSTakashi Iwai struct snd_info_private_data *data; 774adb7bcbSTakashi Iwai 784adb7bcbSTakashi Iwai if (!entry || !entry->p) 794adb7bcbSTakashi Iwai return -ENODEV; 804adb7bcbSTakashi Iwai if (!try_module_get(entry->module)) 814adb7bcbSTakashi Iwai return -EFAULT; 824adb7bcbSTakashi Iwai data = kzalloc(sizeof(*data), GFP_KERNEL); 834adb7bcbSTakashi Iwai if (!data) { 844adb7bcbSTakashi Iwai module_put(entry->module); 854adb7bcbSTakashi Iwai return -ENOMEM; 864adb7bcbSTakashi Iwai } 874adb7bcbSTakashi Iwai data->entry = entry; 884adb7bcbSTakashi Iwai *ret = data; 894adb7bcbSTakashi Iwai return 0; 904adb7bcbSTakashi Iwai } 914adb7bcbSTakashi Iwai 924adb7bcbSTakashi Iwai static bool valid_pos(loff_t pos, size_t count) 934adb7bcbSTakashi Iwai { 944adb7bcbSTakashi Iwai if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 954adb7bcbSTakashi Iwai return false; 964adb7bcbSTakashi Iwai if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 974adb7bcbSTakashi Iwai return false; 984adb7bcbSTakashi Iwai return true; 994adb7bcbSTakashi Iwai } 1004adb7bcbSTakashi Iwai 1014adb7bcbSTakashi Iwai /* 1024adb7bcbSTakashi Iwai * file ops for binary proc files 1034adb7bcbSTakashi Iwai */ 1041da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) 1051da177e4SLinus Torvalds { 10624c1f931STakashi Iwai struct snd_info_private_data *data; 1071da177e4SLinus Torvalds struct snd_info_entry *entry; 10873029e0fSTakashi Iwai loff_t ret = -EINVAL, size; 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds data = file->private_data; 1111da177e4SLinus Torvalds entry = data->entry; 1125b5cd553STakashi Iwai mutex_lock(&entry->access); 1134adb7bcbSTakashi Iwai if (entry->c.ops->llseek) { 11473029e0fSTakashi Iwai offset = entry->c.ops->llseek(entry, 1151da177e4SLinus Torvalds data->file_private_data, 1161da177e4SLinus Torvalds file, offset, orig); 1171da177e4SLinus Torvalds goto out; 1181da177e4SLinus Torvalds } 1194adb7bcbSTakashi Iwai 12073029e0fSTakashi Iwai size = entry->size; 12173029e0fSTakashi Iwai switch (orig) { 12273029e0fSTakashi Iwai case SEEK_SET: 1231da177e4SLinus Torvalds break; 12473029e0fSTakashi Iwai case SEEK_CUR: 12573029e0fSTakashi Iwai offset += file->f_pos; 12673029e0fSTakashi Iwai break; 12773029e0fSTakashi Iwai case SEEK_END: 12873029e0fSTakashi Iwai if (!size) 12973029e0fSTakashi Iwai goto out; 13073029e0fSTakashi Iwai offset += size; 13173029e0fSTakashi Iwai break; 13273029e0fSTakashi Iwai default: 13373029e0fSTakashi Iwai goto out; 1341da177e4SLinus Torvalds } 13573029e0fSTakashi Iwai if (offset < 0) 13673029e0fSTakashi Iwai goto out; 13773029e0fSTakashi Iwai if (size && offset > size) 13873029e0fSTakashi Iwai offset = size; 13973029e0fSTakashi Iwai file->f_pos = offset; 14073029e0fSTakashi Iwai ret = offset; 1411da177e4SLinus Torvalds out: 1425b5cd553STakashi Iwai mutex_unlock(&entry->access); 1431da177e4SLinus Torvalds return ret; 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, 1471da177e4SLinus Torvalds size_t count, loff_t * offset) 1481da177e4SLinus Torvalds { 1494adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 1504adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 1514adb7bcbSTakashi Iwai size_t size; 1521da177e4SLinus Torvalds loff_t pos; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds pos = *offset; 1554adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 1561da177e4SLinus Torvalds return -EIO; 157d97e1b78STakashi Iwai if (pos >= entry->size) 158d97e1b78STakashi Iwai return 0; 159d97e1b78STakashi Iwai size = entry->size - pos; 160d97e1b78STakashi Iwai size = min(count, size); 1614adb7bcbSTakashi Iwai size = entry->c.ops->read(entry, data->file_private_data, 162d97e1b78STakashi Iwai file, buffer, size, pos); 1631da177e4SLinus Torvalds if ((ssize_t) size > 0) 1641da177e4SLinus Torvalds *offset = pos + size; 1651da177e4SLinus Torvalds return size; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, 1691da177e4SLinus Torvalds size_t count, loff_t * offset) 1701da177e4SLinus Torvalds { 1714adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 1724adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 1737e4eeec8STakashi Iwai ssize_t size = 0; 1741da177e4SLinus Torvalds loff_t pos; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds pos = *offset; 1774adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 1781da177e4SLinus Torvalds return -EIO; 1794adb7bcbSTakashi Iwai if (count > 0) { 180d97e1b78STakashi Iwai size_t maxsize = entry->size - pos; 181d97e1b78STakashi Iwai count = min(count, maxsize); 1824adb7bcbSTakashi Iwai size = entry->c.ops->write(entry, data->file_private_data, 1831da177e4SLinus Torvalds file, buffer, count, pos); 184d97e1b78STakashi Iwai } 1854adb7bcbSTakashi Iwai if (size > 0) 1861da177e4SLinus Torvalds *offset = pos + size; 1871da177e4SLinus Torvalds return size; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 190680ef72aSAl Viro static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait) 1911da177e4SLinus Torvalds { 1924adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 1934adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 194680ef72aSAl Viro __poll_t mask = 0; 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds if (entry->c.ops->poll) 1971da177e4SLinus Torvalds return entry->c.ops->poll(entry, 1981da177e4SLinus Torvalds data->file_private_data, 1991da177e4SLinus Torvalds file, wait); 2001da177e4SLinus Torvalds if (entry->c.ops->read) 201a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM; 2021da177e4SLinus Torvalds if (entry->c.ops->write) 203a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM; 2041da177e4SLinus Torvalds return mask; 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 207d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, 208d99e9889SIngo Molnar unsigned long arg) 2091da177e4SLinus Torvalds { 2104adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2114adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 2121da177e4SLinus Torvalds 2134adb7bcbSTakashi Iwai if (!entry->c.ops->ioctl) 2141da177e4SLinus Torvalds return -ENOTTY; 2154adb7bcbSTakashi Iwai return entry->c.ops->ioctl(entry, data->file_private_data, 2164adb7bcbSTakashi Iwai file, cmd, arg); 2171da177e4SLinus Torvalds } 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) 2201da177e4SLinus Torvalds { 221496ad9aaSAl Viro struct inode *inode = file_inode(file); 22224c1f931STakashi Iwai struct snd_info_private_data *data; 2231da177e4SLinus Torvalds struct snd_info_entry *entry; 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds data = file->private_data; 2261da177e4SLinus Torvalds if (data == NULL) 2271da177e4SLinus Torvalds return 0; 2281da177e4SLinus Torvalds entry = data->entry; 2294adb7bcbSTakashi Iwai if (!entry->c.ops->mmap) 2301da177e4SLinus Torvalds return -ENXIO; 2314adb7bcbSTakashi Iwai return entry->c.ops->mmap(entry, data->file_private_data, 2324adb7bcbSTakashi Iwai inode, file, vma); 2334adb7bcbSTakashi Iwai } 2344adb7bcbSTakashi Iwai 2354adb7bcbSTakashi Iwai static int snd_info_entry_open(struct inode *inode, struct file *file) 2364adb7bcbSTakashi Iwai { 237*359745d7SMuchun Song struct snd_info_entry *entry = pde_data(inode); 2384adb7bcbSTakashi Iwai struct snd_info_private_data *data; 2394adb7bcbSTakashi Iwai int mode, err; 2404adb7bcbSTakashi Iwai 2414adb7bcbSTakashi Iwai mutex_lock(&info_mutex); 2424adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data); 2434adb7bcbSTakashi Iwai if (err < 0) 2444adb7bcbSTakashi Iwai goto unlock; 2454adb7bcbSTakashi Iwai 2464adb7bcbSTakashi Iwai mode = file->f_flags & O_ACCMODE; 2474adb7bcbSTakashi Iwai if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) || 2484adb7bcbSTakashi Iwai ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) { 2494adb7bcbSTakashi Iwai err = -ENODEV; 2504adb7bcbSTakashi Iwai goto error; 2514adb7bcbSTakashi Iwai } 2524adb7bcbSTakashi Iwai 2534adb7bcbSTakashi Iwai if (entry->c.ops->open) { 2544adb7bcbSTakashi Iwai err = entry->c.ops->open(entry, mode, &data->file_private_data); 2554adb7bcbSTakashi Iwai if (err < 0) 2564adb7bcbSTakashi Iwai goto error; 2574adb7bcbSTakashi Iwai } 2584adb7bcbSTakashi Iwai 2594adb7bcbSTakashi Iwai file->private_data = data; 2604adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 2614adb7bcbSTakashi Iwai return 0; 2624adb7bcbSTakashi Iwai 2634adb7bcbSTakashi Iwai error: 2644adb7bcbSTakashi Iwai kfree(data); 2654adb7bcbSTakashi Iwai module_put(entry->module); 2664adb7bcbSTakashi Iwai unlock: 2674adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 2684adb7bcbSTakashi Iwai return err; 2694adb7bcbSTakashi Iwai } 2704adb7bcbSTakashi Iwai 2714adb7bcbSTakashi Iwai static int snd_info_entry_release(struct inode *inode, struct file *file) 2724adb7bcbSTakashi Iwai { 2734adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data; 2744adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 2754adb7bcbSTakashi Iwai 2764adb7bcbSTakashi Iwai if (entry->c.ops->release) 2774adb7bcbSTakashi Iwai entry->c.ops->release(entry, file->f_flags & O_ACCMODE, 2784adb7bcbSTakashi Iwai data->file_private_data); 2794adb7bcbSTakashi Iwai module_put(entry->module); 2804adb7bcbSTakashi Iwai kfree(data); 2814adb7bcbSTakashi Iwai return 0; 2821da177e4SLinus Torvalds } 2831da177e4SLinus Torvalds 28497a32539SAlexey Dobriyan static const struct proc_ops snd_info_entry_operations = 2851da177e4SLinus Torvalds { 28697a32539SAlexey Dobriyan .proc_lseek = snd_info_entry_llseek, 28797a32539SAlexey Dobriyan .proc_read = snd_info_entry_read, 28897a32539SAlexey Dobriyan .proc_write = snd_info_entry_write, 28997a32539SAlexey Dobriyan .proc_poll = snd_info_entry_poll, 29097a32539SAlexey Dobriyan .proc_ioctl = snd_info_entry_ioctl, 29197a32539SAlexey Dobriyan .proc_mmap = snd_info_entry_mmap, 29297a32539SAlexey Dobriyan .proc_open = snd_info_entry_open, 29397a32539SAlexey Dobriyan .proc_release = snd_info_entry_release, 2941da177e4SLinus Torvalds }; 2951da177e4SLinus Torvalds 2964adb7bcbSTakashi Iwai /* 2974adb7bcbSTakashi Iwai * file ops for text proc files 2984adb7bcbSTakashi Iwai */ 2994adb7bcbSTakashi Iwai static ssize_t snd_info_text_entry_write(struct file *file, 3004adb7bcbSTakashi Iwai const char __user *buffer, 3014adb7bcbSTakashi Iwai size_t count, loff_t *offset) 3024adb7bcbSTakashi Iwai { 3034adb7bcbSTakashi Iwai struct seq_file *m = file->private_data; 3044adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private; 3054adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 3064adb7bcbSTakashi Iwai struct snd_info_buffer *buf; 3074adb7bcbSTakashi Iwai loff_t pos; 3084adb7bcbSTakashi Iwai size_t next; 3094adb7bcbSTakashi Iwai int err = 0; 3104adb7bcbSTakashi Iwai 3116809cd68STakashi Iwai if (!entry->c.text.write) 3126809cd68STakashi Iwai return -EIO; 3134adb7bcbSTakashi Iwai pos = *offset; 3144adb7bcbSTakashi Iwai if (!valid_pos(pos, count)) 3154adb7bcbSTakashi Iwai return -EIO; 3164adb7bcbSTakashi Iwai next = pos + count; 317027a9fe6STakashi Iwai /* don't handle too large text inputs */ 318027a9fe6STakashi Iwai if (next > 16 * 1024) 319027a9fe6STakashi Iwai return -EIO; 3204adb7bcbSTakashi Iwai mutex_lock(&entry->access); 3214adb7bcbSTakashi Iwai buf = data->wbuffer; 3224adb7bcbSTakashi Iwai if (!buf) { 3234adb7bcbSTakashi Iwai data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3244adb7bcbSTakashi Iwai if (!buf) { 3254adb7bcbSTakashi Iwai err = -ENOMEM; 3264adb7bcbSTakashi Iwai goto error; 3274adb7bcbSTakashi Iwai } 3284adb7bcbSTakashi Iwai } 3294adb7bcbSTakashi Iwai if (next > buf->len) { 330ffb73b08STakashi Iwai char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL); 3314adb7bcbSTakashi Iwai if (!nbuf) { 3324adb7bcbSTakashi Iwai err = -ENOMEM; 3334adb7bcbSTakashi Iwai goto error; 3344adb7bcbSTakashi Iwai } 335ffb73b08STakashi Iwai kvfree(buf->buffer); 3364adb7bcbSTakashi Iwai buf->buffer = nbuf; 3374adb7bcbSTakashi Iwai buf->len = PAGE_ALIGN(next); 3384adb7bcbSTakashi Iwai } 3394adb7bcbSTakashi Iwai if (copy_from_user(buf->buffer + pos, buffer, count)) { 3404adb7bcbSTakashi Iwai err = -EFAULT; 3414adb7bcbSTakashi Iwai goto error; 3424adb7bcbSTakashi Iwai } 3434adb7bcbSTakashi Iwai buf->size = next; 3444adb7bcbSTakashi Iwai error: 3454adb7bcbSTakashi Iwai mutex_unlock(&entry->access); 3464adb7bcbSTakashi Iwai if (err < 0) 3474adb7bcbSTakashi Iwai return err; 3484adb7bcbSTakashi Iwai *offset = next; 3494adb7bcbSTakashi Iwai return count; 3504adb7bcbSTakashi Iwai } 3514adb7bcbSTakashi Iwai 3524adb7bcbSTakashi Iwai static int snd_info_seq_show(struct seq_file *seq, void *p) 3534adb7bcbSTakashi Iwai { 3544adb7bcbSTakashi Iwai struct snd_info_private_data *data = seq->private; 3554adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 3564adb7bcbSTakashi Iwai 3576809cd68STakashi Iwai if (!entry->c.text.read) { 3586809cd68STakashi Iwai return -EIO; 3596809cd68STakashi Iwai } else { 3604adb7bcbSTakashi Iwai data->rbuffer->buffer = (char *)seq; /* XXX hack! */ 3614adb7bcbSTakashi Iwai entry->c.text.read(entry, data->rbuffer); 3624adb7bcbSTakashi Iwai } 3634adb7bcbSTakashi Iwai return 0; 3644adb7bcbSTakashi Iwai } 3654adb7bcbSTakashi Iwai 3664adb7bcbSTakashi Iwai static int snd_info_text_entry_open(struct inode *inode, struct file *file) 3674adb7bcbSTakashi Iwai { 368*359745d7SMuchun Song struct snd_info_entry *entry = pde_data(inode); 3694adb7bcbSTakashi Iwai struct snd_info_private_data *data; 3704adb7bcbSTakashi Iwai int err; 3714adb7bcbSTakashi Iwai 3724adb7bcbSTakashi Iwai mutex_lock(&info_mutex); 3734adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data); 3744adb7bcbSTakashi Iwai if (err < 0) 3754adb7bcbSTakashi Iwai goto unlock; 3764adb7bcbSTakashi Iwai 3774adb7bcbSTakashi Iwai data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL); 3784adb7bcbSTakashi Iwai if (!data->rbuffer) { 3794adb7bcbSTakashi Iwai err = -ENOMEM; 3804adb7bcbSTakashi Iwai goto error; 3814adb7bcbSTakashi Iwai } 3824adb7bcbSTakashi Iwai if (entry->size) 3834adb7bcbSTakashi Iwai err = single_open_size(file, snd_info_seq_show, data, 3844adb7bcbSTakashi Iwai entry->size); 3854adb7bcbSTakashi Iwai else 3864adb7bcbSTakashi Iwai err = single_open(file, snd_info_seq_show, data); 3874adb7bcbSTakashi Iwai if (err < 0) 3884adb7bcbSTakashi Iwai goto error; 3894adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 3904adb7bcbSTakashi Iwai return 0; 3914adb7bcbSTakashi Iwai 3924adb7bcbSTakashi Iwai error: 3934adb7bcbSTakashi Iwai kfree(data->rbuffer); 3944adb7bcbSTakashi Iwai kfree(data); 3954adb7bcbSTakashi Iwai module_put(entry->module); 3964adb7bcbSTakashi Iwai unlock: 3974adb7bcbSTakashi Iwai mutex_unlock(&info_mutex); 3984adb7bcbSTakashi Iwai return err; 3994adb7bcbSTakashi Iwai } 4004adb7bcbSTakashi Iwai 4014adb7bcbSTakashi Iwai static int snd_info_text_entry_release(struct inode *inode, struct file *file) 4024adb7bcbSTakashi Iwai { 4034adb7bcbSTakashi Iwai struct seq_file *m = file->private_data; 4044adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private; 4054adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry; 4064adb7bcbSTakashi Iwai 4074adb7bcbSTakashi Iwai if (data->wbuffer && entry->c.text.write) 4084adb7bcbSTakashi Iwai entry->c.text.write(entry, data->wbuffer); 4094adb7bcbSTakashi Iwai 4104adb7bcbSTakashi Iwai single_release(inode, file); 4114adb7bcbSTakashi Iwai kfree(data->rbuffer); 4124adb7bcbSTakashi Iwai if (data->wbuffer) { 413ffb73b08STakashi Iwai kvfree(data->wbuffer->buffer); 4144adb7bcbSTakashi Iwai kfree(data->wbuffer); 4154adb7bcbSTakashi Iwai } 4164adb7bcbSTakashi Iwai 4174adb7bcbSTakashi Iwai module_put(entry->module); 4184adb7bcbSTakashi Iwai kfree(data); 4194adb7bcbSTakashi Iwai return 0; 4204adb7bcbSTakashi Iwai } 4214adb7bcbSTakashi Iwai 42297a32539SAlexey Dobriyan static const struct proc_ops snd_info_text_entry_ops = 4234adb7bcbSTakashi Iwai { 42497a32539SAlexey Dobriyan .proc_open = snd_info_text_entry_open, 42597a32539SAlexey Dobriyan .proc_release = snd_info_text_entry_release, 42697a32539SAlexey Dobriyan .proc_write = snd_info_text_entry_write, 42797a32539SAlexey Dobriyan .proc_lseek = seq_lseek, 42897a32539SAlexey Dobriyan .proc_read = seq_read, 4294adb7bcbSTakashi Iwai }; 4304adb7bcbSTakashi Iwai 431886364f6STakashi Iwai static struct snd_info_entry *create_subdir(struct module *mod, 432886364f6STakashi Iwai const char *name) 433886364f6STakashi Iwai { 434886364f6STakashi Iwai struct snd_info_entry *entry; 435886364f6STakashi Iwai 436886364f6STakashi Iwai entry = snd_info_create_module_entry(mod, name, NULL); 437886364f6STakashi Iwai if (!entry) 438886364f6STakashi Iwai return NULL; 4396a73cf46SJoe Perches entry->mode = S_IFDIR | 0555; 440886364f6STakashi Iwai if (snd_info_register(entry) < 0) { 441886364f6STakashi Iwai snd_info_free_entry(entry); 442886364f6STakashi Iwai return NULL; 443886364f6STakashi Iwai } 444886364f6STakashi Iwai return entry; 445886364f6STakashi Iwai } 446886364f6STakashi Iwai 4478e7ccb7bSTakashi Iwai static struct snd_info_entry * 448a858ee66STakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent, 449a858ee66STakashi Iwai struct module *module); 450644dbd64STakashi Iwai 4511da177e4SLinus Torvalds int __init snd_info_init(void) 4521da177e4SLinus Torvalds { 453a858ee66STakashi Iwai snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE); 454644dbd64STakashi Iwai if (!snd_proc_root) 4551da177e4SLinus Torvalds return -ENOMEM; 4566a73cf46SJoe Perches snd_proc_root->mode = S_IFDIR | 0555; 457644dbd64STakashi Iwai snd_proc_root->p = proc_mkdir("asound", NULL); 458644dbd64STakashi Iwai if (!snd_proc_root->p) 459644dbd64STakashi Iwai goto error; 4601da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 461886364f6STakashi Iwai snd_oss_root = create_subdir(THIS_MODULE, "oss"); 462886364f6STakashi Iwai if (!snd_oss_root) 463886364f6STakashi Iwai goto error; 4641da177e4SLinus Torvalds #endif 4658eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER) 466886364f6STakashi Iwai snd_seq_root = create_subdir(THIS_MODULE, "seq"); 467886364f6STakashi Iwai if (!snd_seq_root) 468886364f6STakashi Iwai goto error; 4691da177e4SLinus Torvalds #endif 470b591b6e9STakashi Iwai if (snd_info_version_init() < 0 || 471b591b6e9STakashi Iwai snd_minor_info_init() < 0 || 472b591b6e9STakashi Iwai snd_minor_info_oss_init() < 0 || 473a0dca822STakashi Iwai snd_card_info_init() < 0 || 474a0dca822STakashi Iwai snd_info_minor_register() < 0) 475b591b6e9STakashi Iwai goto error; 4761da177e4SLinus Torvalds return 0; 477886364f6STakashi Iwai 478886364f6STakashi Iwai error: 479644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root); 480886364f6STakashi Iwai return -ENOMEM; 4811da177e4SLinus Torvalds } 4821da177e4SLinus Torvalds 4831da177e4SLinus Torvalds int __exit snd_info_done(void) 4841da177e4SLinus Torvalds { 485644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root); 4861da177e4SLinus Torvalds return 0; 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds 48929b2625fSTakashi Iwai static void snd_card_id_read(struct snd_info_entry *entry, 49029b2625fSTakashi Iwai struct snd_info_buffer *buffer) 49129b2625fSTakashi Iwai { 49229b2625fSTakashi Iwai struct snd_card *card = entry->private_data; 49329b2625fSTakashi Iwai 49429b2625fSTakashi Iwai snd_iprintf(buffer, "%s\n", card->id); 49529b2625fSTakashi Iwai } 49629b2625fSTakashi Iwai 4971da177e4SLinus Torvalds /* 4981da177e4SLinus Torvalds * create a card proc file 4991da177e4SLinus Torvalds * called from init.c 5001da177e4SLinus Torvalds */ 50124c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card) 5021da177e4SLinus Torvalds { 5031da177e4SLinus Torvalds char str[8]; 50424c1f931STakashi Iwai struct snd_info_entry *entry; 5051da177e4SLinus Torvalds 5067eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 5077eaa943cSTakashi Iwai return -ENXIO; 5081da177e4SLinus Torvalds 5091da177e4SLinus Torvalds sprintf(str, "card%i", card->number); 510886364f6STakashi Iwai entry = create_subdir(card->module, str); 511886364f6STakashi Iwai if (!entry) 5121da177e4SLinus Torvalds return -ENOMEM; 5131da177e4SLinus Torvalds card->proc_root = entry; 51429b2625fSTakashi Iwai 51529b2625fSTakashi Iwai return snd_card_ro_proc_new(card, "id", card, snd_card_id_read); 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 5181da177e4SLinus Torvalds /* 5191da177e4SLinus Torvalds * register the card proc file 5201da177e4SLinus Torvalds * called from init.c 5212471b6c8STakashi Iwai * can be called multiple times for reinitialization 5221da177e4SLinus Torvalds */ 52324c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card) 5241da177e4SLinus Torvalds { 5251da177e4SLinus Torvalds struct proc_dir_entry *p; 5262471b6c8STakashi Iwai int err; 5271da177e4SLinus Torvalds 5287eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 5297eaa943cSTakashi Iwai return -ENXIO; 5301da177e4SLinus Torvalds 531348c5ad5STakashi Iwai err = snd_info_register(card->proc_root); 5322471b6c8STakashi Iwai if (err < 0) 5332471b6c8STakashi Iwai return err; 5342471b6c8STakashi Iwai 5351da177e4SLinus Torvalds if (!strcmp(card->id, card->proc_root->name)) 5361da177e4SLinus Torvalds return 0; 5371da177e4SLinus Torvalds 5382471b6c8STakashi Iwai if (card->proc_root_link) 5392471b6c8STakashi Iwai return 0; 540644dbd64STakashi Iwai p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name); 5412471b6c8STakashi Iwai if (!p) 5421da177e4SLinus Torvalds return -ENOMEM; 5431da177e4SLinus Torvalds card->proc_root_link = p; 5441da177e4SLinus Torvalds return 0; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds /* 548c2eb9c4eSJaroslav Kysela * called on card->id change 549c2eb9c4eSJaroslav Kysela */ 550c2eb9c4eSJaroslav Kysela void snd_info_card_id_change(struct snd_card *card) 551c2eb9c4eSJaroslav Kysela { 552c2eb9c4eSJaroslav Kysela mutex_lock(&info_mutex); 553c2eb9c4eSJaroslav Kysela if (card->proc_root_link) { 554a8ca16eaSDavid Howells proc_remove(card->proc_root_link); 555c2eb9c4eSJaroslav Kysela card->proc_root_link = NULL; 556c2eb9c4eSJaroslav Kysela } 557c2eb9c4eSJaroslav Kysela if (strcmp(card->id, card->proc_root->name)) 558c2eb9c4eSJaroslav Kysela card->proc_root_link = proc_symlink(card->id, 559644dbd64STakashi Iwai snd_proc_root->p, 560c2eb9c4eSJaroslav Kysela card->proc_root->name); 561c2eb9c4eSJaroslav Kysela mutex_unlock(&info_mutex); 562c2eb9c4eSJaroslav Kysela } 563c2eb9c4eSJaroslav Kysela 564c2eb9c4eSJaroslav Kysela /* 5651da177e4SLinus Torvalds * de-register the card proc file 5661da177e4SLinus Torvalds * called from init.c 5671da177e4SLinus Torvalds */ 568746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card) 5691da177e4SLinus Torvalds { 5707eaa943cSTakashi Iwai if (!card) 5717eaa943cSTakashi Iwai return; 572746d4a02STakashi Iwai mutex_lock(&info_mutex); 573a8ca16eaSDavid Howells proc_remove(card->proc_root_link); 5741da177e4SLinus Torvalds card->proc_root_link = NULL; 575746d4a02STakashi Iwai if (card->proc_root) 576746d4a02STakashi Iwai snd_info_disconnect(card->proc_root); 577746d4a02STakashi Iwai mutex_unlock(&info_mutex); 5781da177e4SLinus Torvalds } 579746d4a02STakashi Iwai 580746d4a02STakashi Iwai /* 581746d4a02STakashi Iwai * release the card proc file resources 582746d4a02STakashi Iwai * called from init.c 583746d4a02STakashi Iwai */ 584746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card) 585746d4a02STakashi Iwai { 5867eaa943cSTakashi Iwai if (!card) 5877eaa943cSTakashi Iwai return 0; 588746d4a02STakashi Iwai snd_info_free_entry(card->proc_root); 589746d4a02STakashi Iwai card->proc_root = NULL; 5901da177e4SLinus Torvalds return 0; 5911da177e4SLinus Torvalds } 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds 5941da177e4SLinus Torvalds /** 5951da177e4SLinus Torvalds * snd_info_get_line - read one line from the procfs buffer 5961da177e4SLinus Torvalds * @buffer: the procfs buffer 5971da177e4SLinus Torvalds * @line: the buffer to store 598ddc64b27SClemens Ladisch * @len: the max. buffer size 5991da177e4SLinus Torvalds * 6001da177e4SLinus Torvalds * Reads one line from the buffer and stores the string. 6011da177e4SLinus Torvalds * 602eb7c06e8SYacine Belkadi * Return: Zero if successful, or 1 if error or EOF. 6031da177e4SLinus Torvalds */ 60424c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) 6051da177e4SLinus Torvalds { 6060e023687SColin Ian King int c; 6071da177e4SLinus Torvalds 60860379ba0STakashi Iwai if (snd_BUG_ON(!buffer)) 60960379ba0STakashi Iwai return 1; 61060379ba0STakashi Iwai if (!buffer->buffer) 6110bc0ec90STakashi Iwai return 1; 6121da177e4SLinus Torvalds if (len <= 0 || buffer->stop || buffer->error) 6131da177e4SLinus Torvalds return 1; 6140bc0ec90STakashi Iwai while (!buffer->stop) { 6157e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++]; 6167e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) 6171da177e4SLinus Torvalds buffer->stop = 1; 6180bc0ec90STakashi Iwai if (c == '\n') 6191da177e4SLinus Torvalds break; 620ddc64b27SClemens Ladisch if (len > 1) { 6210bc0ec90STakashi Iwai len--; 6221da177e4SLinus Torvalds *line++ = c; 6231da177e4SLinus Torvalds } 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds *line = '\0'; 6261da177e4SLinus Torvalds return 0; 6271da177e4SLinus Torvalds } 628c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line); 629c0d3fb39STakashi Iwai 6301da177e4SLinus Torvalds /** 631856def8aSHenrik Kretzschmar * snd_info_get_str - parse a string token 6321da177e4SLinus Torvalds * @dest: the buffer to store the string token 6331da177e4SLinus Torvalds * @src: the original string 6341da177e4SLinus Torvalds * @len: the max. length of token - 1 6351da177e4SLinus Torvalds * 6361da177e4SLinus Torvalds * Parses the original string and copy a token to the given 6371da177e4SLinus Torvalds * string buffer. 6381da177e4SLinus Torvalds * 639eb7c06e8SYacine Belkadi * Return: The updated pointer of the original string so that 6401da177e4SLinus Torvalds * it can be used for the next call. 6411da177e4SLinus Torvalds */ 6424f7454a9STakashi Iwai const char *snd_info_get_str(char *dest, const char *src, int len) 6431da177e4SLinus Torvalds { 6441da177e4SLinus Torvalds int c; 6451da177e4SLinus Torvalds 6461da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 6471da177e4SLinus Torvalds src++; 6481da177e4SLinus Torvalds if (*src == '"' || *src == '\'') { 6491da177e4SLinus Torvalds c = *src++; 6501da177e4SLinus Torvalds while (--len > 0 && *src && *src != c) { 6511da177e4SLinus Torvalds *dest++ = *src++; 6521da177e4SLinus Torvalds } 6531da177e4SLinus Torvalds if (*src == c) 6541da177e4SLinus Torvalds src++; 6551da177e4SLinus Torvalds } else { 6561da177e4SLinus Torvalds while (--len > 0 && *src && *src != ' ' && *src != '\t') { 6571da177e4SLinus Torvalds *dest++ = *src++; 6581da177e4SLinus Torvalds } 6591da177e4SLinus Torvalds } 6601da177e4SLinus Torvalds *dest = 0; 6611da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 6621da177e4SLinus Torvalds src++; 6631da177e4SLinus Torvalds return src; 6641da177e4SLinus Torvalds } 665c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str); 666c0d3fb39STakashi Iwai 667c309c467STakashi Iwai /* 6681da177e4SLinus Torvalds * snd_info_create_entry - create an info entry 6691da177e4SLinus Torvalds * @name: the proc file name 6708e7ccb7bSTakashi Iwai * @parent: the parent directory 6711da177e4SLinus Torvalds * 6721da177e4SLinus Torvalds * Creates an info entry with the given file name and initializes as 6731da177e4SLinus Torvalds * the default state. 6741da177e4SLinus Torvalds * 6751da177e4SLinus Torvalds * Usually called from other functions such as 6761da177e4SLinus Torvalds * snd_info_create_card_entry(). 6771da177e4SLinus Torvalds * 678eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 6791da177e4SLinus Torvalds */ 6808e7ccb7bSTakashi Iwai static struct snd_info_entry * 681a858ee66STakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent, 682a858ee66STakashi Iwai struct module *module) 6831da177e4SLinus Torvalds { 68424c1f931STakashi Iwai struct snd_info_entry *entry; 685ca2c0966STakashi Iwai entry = kzalloc(sizeof(*entry), GFP_KERNEL); 6861da177e4SLinus Torvalds if (entry == NULL) 6871da177e4SLinus Torvalds return NULL; 688543537bdSPaulo Marques entry->name = kstrdup(name, GFP_KERNEL); 6891da177e4SLinus Torvalds if (entry->name == NULL) { 6901da177e4SLinus Torvalds kfree(entry); 6911da177e4SLinus Torvalds return NULL; 6921da177e4SLinus Torvalds } 6936a73cf46SJoe Perches entry->mode = S_IFREG | 0444; 6941da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_TEXT; 6951a60d4c5SIngo Molnar mutex_init(&entry->access); 696746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->children); 697746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->list); 6988e7ccb7bSTakashi Iwai entry->parent = parent; 699a858ee66STakashi Iwai entry->module = module; 7008c2f8708STakashi Iwai if (parent) { 7018c2f8708STakashi Iwai mutex_lock(&parent->access); 7028e7ccb7bSTakashi Iwai list_add_tail(&entry->list, &parent->children); 7038c2f8708STakashi Iwai mutex_unlock(&parent->access); 7048c2f8708STakashi Iwai } 7051da177e4SLinus Torvalds return entry; 7061da177e4SLinus Torvalds } 7071da177e4SLinus Torvalds 7081da177e4SLinus Torvalds /** 7091da177e4SLinus Torvalds * snd_info_create_module_entry - create an info entry for the given module 7101da177e4SLinus Torvalds * @module: the module pointer 7111da177e4SLinus Torvalds * @name: the file name 7121da177e4SLinus Torvalds * @parent: the parent directory 7131da177e4SLinus Torvalds * 7141da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given module. 7151da177e4SLinus Torvalds * 716eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 7171da177e4SLinus Torvalds */ 71824c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module, 7191da177e4SLinus Torvalds const char *name, 72024c1f931STakashi Iwai struct snd_info_entry *parent) 7211da177e4SLinus Torvalds { 7223a554371STakashi Iwai if (!parent) 7233a554371STakashi Iwai parent = snd_proc_root; 724a858ee66STakashi Iwai return snd_info_create_entry(name, parent, module); 7251da177e4SLinus Torvalds } 726c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry); 727c0d3fb39STakashi Iwai 7281da177e4SLinus Torvalds /** 7291da177e4SLinus Torvalds * snd_info_create_card_entry - create an info entry for the given card 7301da177e4SLinus Torvalds * @card: the card instance 7311da177e4SLinus Torvalds * @name: the file name 7321da177e4SLinus Torvalds * @parent: the parent directory 7331da177e4SLinus Torvalds * 7341da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card. 7351da177e4SLinus Torvalds * 736eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure. 7371da177e4SLinus Torvalds */ 73824c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, 7391da177e4SLinus Torvalds const char *name, 74024c1f931STakashi Iwai struct snd_info_entry * parent) 7411da177e4SLinus Torvalds { 7423a554371STakashi Iwai if (!parent) 7433a554371STakashi Iwai parent = card->proc_root; 744a858ee66STakashi Iwai return snd_info_create_entry(name, parent, card->module); 7451da177e4SLinus Torvalds } 746c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry); 747c0d3fb39STakashi Iwai 748746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry) 749746d4a02STakashi Iwai { 75090a409aaSTakashi Iwai struct snd_info_entry *p; 751746d4a02STakashi Iwai 752746d4a02STakashi Iwai if (!entry->p) 753746d4a02STakashi Iwai return; 75490a409aaSTakashi Iwai list_for_each_entry(p, &entry->children, list) 755c560a679STakashi Iwai snd_info_disconnect(p); 756a8ca16eaSDavid Howells proc_remove(entry->p); 757746d4a02STakashi Iwai entry->p = NULL; 758746d4a02STakashi Iwai } 759746d4a02STakashi Iwai 7601da177e4SLinus Torvalds /** 7611da177e4SLinus Torvalds * snd_info_free_entry - release the info entry 7621da177e4SLinus Torvalds * @entry: the info entry 7631da177e4SLinus Torvalds * 764c560a679STakashi Iwai * Releases the info entry. 7651da177e4SLinus Torvalds */ 76624c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry) 7671da177e4SLinus Torvalds { 768c560a679STakashi Iwai struct snd_info_entry *p, *n; 769c560a679STakashi Iwai 770c560a679STakashi Iwai if (!entry) 7711da177e4SLinus Torvalds return; 772746d4a02STakashi Iwai if (entry->p) { 773746d4a02STakashi Iwai mutex_lock(&info_mutex); 774746d4a02STakashi Iwai snd_info_disconnect(entry); 775746d4a02STakashi Iwai mutex_unlock(&info_mutex); 776746d4a02STakashi Iwai } 777c560a679STakashi Iwai 778c560a679STakashi Iwai /* free all children at first */ 779c560a679STakashi Iwai list_for_each_entry_safe(p, n, &entry->children, list) 780c560a679STakashi Iwai snd_info_free_entry(p); 781c560a679STakashi Iwai 7828c2f8708STakashi Iwai p = entry->parent; 7838c2f8708STakashi Iwai if (p) { 7848c2f8708STakashi Iwai mutex_lock(&p->access); 78590a409aaSTakashi Iwai list_del(&entry->list); 7868c2f8708STakashi Iwai mutex_unlock(&p->access); 7878c2f8708STakashi Iwai } 7881da177e4SLinus Torvalds kfree(entry->name); 7891da177e4SLinus Torvalds if (entry->private_free) 7901da177e4SLinus Torvalds entry->private_free(entry); 7911da177e4SLinus Torvalds kfree(entry); 7921da177e4SLinus Torvalds } 793c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry); 794c0d3fb39STakashi Iwai 795348c5ad5STakashi Iwai static int __snd_info_register(struct snd_info_entry *entry) 7961da177e4SLinus Torvalds { 7971da177e4SLinus Torvalds struct proc_dir_entry *root, *p = NULL; 7981da177e4SLinus Torvalds 7997eaa943cSTakashi Iwai if (snd_BUG_ON(!entry)) 8007eaa943cSTakashi Iwai return -ENXIO; 801644dbd64STakashi Iwai root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; 8021a60d4c5SIngo Molnar mutex_lock(&info_mutex); 803348c5ad5STakashi Iwai if (entry->p || !root) 804348c5ad5STakashi Iwai goto unlock; 805aee0c612SAl Viro if (S_ISDIR(entry->mode)) { 806aee0c612SAl Viro p = proc_mkdir_mode(entry->name, entry->mode, root); 8071da177e4SLinus Torvalds if (!p) { 8081a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 8091da177e4SLinus Torvalds return -ENOMEM; 8101da177e4SLinus Torvalds } 811aee0c612SAl Viro } else { 81297a32539SAlexey Dobriyan const struct proc_ops *ops; 8134adb7bcbSTakashi Iwai if (entry->content == SNDRV_INFO_CONTENT_DATA) 8144adb7bcbSTakashi Iwai ops = &snd_info_entry_operations; 8154adb7bcbSTakashi Iwai else 8164adb7bcbSTakashi Iwai ops = &snd_info_text_entry_ops; 817aee0c612SAl Viro p = proc_create_data(entry->name, entry->mode, root, 8184adb7bcbSTakashi Iwai ops, entry); 819aee0c612SAl Viro if (!p) { 820aee0c612SAl Viro mutex_unlock(&info_mutex); 821aee0c612SAl Viro return -ENOMEM; 822aee0c612SAl Viro } 823271a15eaSDavid Howells proc_set_size(p, entry->size); 824aee0c612SAl Viro } 8251da177e4SLinus Torvalds entry->p = p; 826348c5ad5STakashi Iwai unlock: 8271a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 8281da177e4SLinus Torvalds return 0; 8291da177e4SLinus Torvalds } 830348c5ad5STakashi Iwai 831348c5ad5STakashi Iwai /** 832348c5ad5STakashi Iwai * snd_info_register - register the info entry 833348c5ad5STakashi Iwai * @entry: the info entry 834348c5ad5STakashi Iwai * 835348c5ad5STakashi Iwai * Registers the proc info entry. 836348c5ad5STakashi Iwai * The all children entries are registered recursively. 837348c5ad5STakashi Iwai * 838348c5ad5STakashi Iwai * Return: Zero if successful, or a negative error code on failure. 839348c5ad5STakashi Iwai */ 840348c5ad5STakashi Iwai int snd_info_register(struct snd_info_entry *entry) 841348c5ad5STakashi Iwai { 842348c5ad5STakashi Iwai struct snd_info_entry *p; 843348c5ad5STakashi Iwai int err; 844348c5ad5STakashi Iwai 845348c5ad5STakashi Iwai if (!entry->p) { 846348c5ad5STakashi Iwai err = __snd_info_register(entry); 847348c5ad5STakashi Iwai if (err < 0) 848348c5ad5STakashi Iwai return err; 849348c5ad5STakashi Iwai } 850348c5ad5STakashi Iwai 851348c5ad5STakashi Iwai list_for_each_entry(p, &entry->children, list) { 852348c5ad5STakashi Iwai err = snd_info_register(p); 853348c5ad5STakashi Iwai if (err < 0) 854348c5ad5STakashi Iwai return err; 855348c5ad5STakashi Iwai } 856348c5ad5STakashi Iwai 857348c5ad5STakashi Iwai return 0; 858348c5ad5STakashi Iwai } 859c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register); 860c0d3fb39STakashi Iwai 8617453e1daSTakashi Iwai /** 8627453e1daSTakashi Iwai * snd_card_rw_proc_new - Create a read/write text proc file entry for the card 8637453e1daSTakashi Iwai * @card: the card instance 8647453e1daSTakashi Iwai * @name: the file name 8657453e1daSTakashi Iwai * @private_data: the arbitrary private data 8667453e1daSTakashi Iwai * @read: the read callback 8677453e1daSTakashi Iwai * @write: the write callback, NULL for read-only 8687453e1daSTakashi Iwai * 8697453e1daSTakashi Iwai * This proc file entry will be registered via snd_card_register() call, and 8707453e1daSTakashi Iwai * it will be removed automatically at the card removal, too. 8717453e1daSTakashi Iwai */ 8727453e1daSTakashi Iwai int snd_card_rw_proc_new(struct snd_card *card, const char *name, 8737453e1daSTakashi Iwai void *private_data, 8747453e1daSTakashi Iwai void (*read)(struct snd_info_entry *, 8757453e1daSTakashi Iwai struct snd_info_buffer *), 8767453e1daSTakashi Iwai void (*write)(struct snd_info_entry *entry, 8777453e1daSTakashi Iwai struct snd_info_buffer *buffer)) 8787453e1daSTakashi Iwai { 8797453e1daSTakashi Iwai struct snd_info_entry *entry; 8807453e1daSTakashi Iwai 8817453e1daSTakashi Iwai entry = snd_info_create_card_entry(card, name, card->proc_root); 8827453e1daSTakashi Iwai if (!entry) 8837453e1daSTakashi Iwai return -ENOMEM; 8847453e1daSTakashi Iwai snd_info_set_text_ops(entry, private_data, read); 8857453e1daSTakashi Iwai if (write) { 8867453e1daSTakashi Iwai entry->mode |= 0200; 8877453e1daSTakashi Iwai entry->c.text.write = write; 8887453e1daSTakashi Iwai } 8897453e1daSTakashi Iwai return 0; 8907453e1daSTakashi Iwai } 8917453e1daSTakashi Iwai EXPORT_SYMBOL_GPL(snd_card_rw_proc_new); 8927453e1daSTakashi Iwai 8931da177e4SLinus Torvalds /* 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds */ 8961da177e4SLinus Torvalds 89724c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 8981da177e4SLinus Torvalds { 8991da177e4SLinus Torvalds snd_iprintf(buffer, 90042662748SJaroslav Kysela "Advanced Linux Sound Architecture Driver Version k%s.\n", 90142662748SJaroslav Kysela init_utsname()->release); 9021da177e4SLinus Torvalds } 9031da177e4SLinus Torvalds 9041da177e4SLinus Torvalds static int __init snd_info_version_init(void) 9051da177e4SLinus Torvalds { 90624c1f931STakashi Iwai struct snd_info_entry *entry; 9071da177e4SLinus Torvalds 9081da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); 9091da177e4SLinus Torvalds if (entry == NULL) 9101da177e4SLinus Torvalds return -ENOMEM; 9111da177e4SLinus Torvalds entry->c.text.read = snd_info_version_read; 912b591b6e9STakashi Iwai return snd_info_register(entry); /* freed in error path */ 9131da177e4SLinus Torvalds } 914