11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Information interface for ALSA driver 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 91da177e4SLinus Torvalds * (at your option) any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 171da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/init.h> 231da177e4SLinus Torvalds #include <linux/time.h> 2427ac792cSAndrea Righi #include <linux/mm.h> 255a0e3ad6STejun Heo #include <linux/slab.h> 26543537bdSPaulo Marques #include <linux/string.h> 27da155d5bSPaul Gortmaker #include <linux/module.h> 281da177e4SLinus Torvalds #include <sound/core.h> 291da177e4SLinus Torvalds #include <sound/minors.h> 301da177e4SLinus Torvalds #include <sound/info.h> 3142662748SJaroslav Kysela #include <linux/utsname.h> 321da177e4SLinus Torvalds #include <linux/proc_fs.h> 331a60d4c5SIngo Molnar #include <linux/mutex.h> 341da177e4SLinus Torvalds #include <stdarg.h> 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds /* 371da177e4SLinus Torvalds * 381da177e4SLinus Torvalds */ 391da177e4SLinus Torvalds 40e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 41e28563ccSTakashi Iwai 421da177e4SLinus Torvalds int snd_info_check_reserved_words(const char *str) 431da177e4SLinus Torvalds { 441da177e4SLinus Torvalds static char *reserved[] = 451da177e4SLinus Torvalds { 461da177e4SLinus Torvalds "version", 471da177e4SLinus Torvalds "meminfo", 481da177e4SLinus Torvalds "memdebug", 491da177e4SLinus Torvalds "detect", 501da177e4SLinus Torvalds "devices", 511da177e4SLinus Torvalds "oss", 521da177e4SLinus Torvalds "cards", 531da177e4SLinus Torvalds "timers", 541da177e4SLinus Torvalds "synth", 551da177e4SLinus Torvalds "pcm", 561da177e4SLinus Torvalds "seq", 571da177e4SLinus Torvalds NULL 581da177e4SLinus Torvalds }; 591da177e4SLinus Torvalds char **xstr = reserved; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds while (*xstr) { 621da177e4SLinus Torvalds if (!strcmp(*xstr, str)) 631da177e4SLinus Torvalds return 0; 641da177e4SLinus Torvalds xstr++; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds if (!strncmp(str, "card", 4)) 671da177e4SLinus Torvalds return 0; 681da177e4SLinus Torvalds return 1; 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex); 721da177e4SLinus Torvalds 7324c1f931STakashi Iwai struct snd_info_private_data { 7424c1f931STakashi Iwai struct snd_info_buffer *rbuffer; 7524c1f931STakashi Iwai struct snd_info_buffer *wbuffer; 7624c1f931STakashi Iwai struct snd_info_entry *entry; 771da177e4SLinus Torvalds void *file_private_data; 7824c1f931STakashi Iwai }; 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds static int snd_info_version_init(void); 811da177e4SLinus Torvalds static int snd_info_version_done(void); 82746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry); 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds 857e4eeec8STakashi Iwai /* resize the proc r/w buffer */ 867e4eeec8STakashi Iwai static int resize_info_buffer(struct snd_info_buffer *buffer, 877e4eeec8STakashi Iwai unsigned int nsize) 887e4eeec8STakashi Iwai { 897e4eeec8STakashi Iwai char *nbuf; 907e4eeec8STakashi Iwai 917e4eeec8STakashi Iwai nsize = PAGE_ALIGN(nsize); 929983aa62STakashi Iwai nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL); 937e4eeec8STakashi Iwai if (! nbuf) 947e4eeec8STakashi Iwai return -ENOMEM; 957e4eeec8STakashi Iwai 967e4eeec8STakashi Iwai buffer->buffer = nbuf; 977e4eeec8STakashi Iwai buffer->len = nsize; 987e4eeec8STakashi Iwai return 0; 997e4eeec8STakashi Iwai } 1007e4eeec8STakashi Iwai 1011da177e4SLinus Torvalds /** 1021da177e4SLinus Torvalds * snd_iprintf - printf on the procfs buffer 1031da177e4SLinus Torvalds * @buffer: the procfs buffer 1041da177e4SLinus Torvalds * @fmt: the printf format 1051da177e4SLinus Torvalds * 1061da177e4SLinus Torvalds * Outputs the string on the procfs buffer just like printf(). 1071da177e4SLinus Torvalds * 1081da177e4SLinus Torvalds * Returns the size of output string. 1091da177e4SLinus Torvalds */ 1104f7454a9STakashi Iwai int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) 1111da177e4SLinus Torvalds { 1121da177e4SLinus Torvalds va_list args; 1131da177e4SLinus Torvalds int len, res; 1147e4eeec8STakashi Iwai int err = 0; 1151da177e4SLinus Torvalds 116f001c3acSTakashi Iwai might_sleep(); 1171da177e4SLinus Torvalds if (buffer->stop || buffer->error) 1181da177e4SLinus Torvalds return 0; 1191da177e4SLinus Torvalds len = buffer->len - buffer->size; 1201da177e4SLinus Torvalds va_start(args, fmt); 1217e4eeec8STakashi Iwai for (;;) { 122dbedca39STakashi Iwai va_list ap; 123dbedca39STakashi Iwai va_copy(ap, args); 124dbedca39STakashi Iwai res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap); 125dbedca39STakashi Iwai va_end(ap); 1267e4eeec8STakashi Iwai if (res < len) 1277e4eeec8STakashi Iwai break; 1287e4eeec8STakashi Iwai err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE); 1297e4eeec8STakashi Iwai if (err < 0) 1307e4eeec8STakashi Iwai break; 1317e4eeec8STakashi Iwai len = buffer->len - buffer->size; 1321da177e4SLinus Torvalds } 1337e4eeec8STakashi Iwai va_end(args); 1347e4eeec8STakashi Iwai 1357e4eeec8STakashi Iwai if (err < 0) 1367e4eeec8STakashi Iwai return err; 1371da177e4SLinus Torvalds buffer->curr += res; 1381da177e4SLinus Torvalds buffer->size += res; 1391da177e4SLinus Torvalds return res; 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds 142c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_iprintf); 143c0d3fb39STakashi Iwai 1441da177e4SLinus Torvalds /* 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds */ 1471da177e4SLinus Torvalds 1486581f4e7STakashi Iwai static struct proc_dir_entry *snd_proc_root; 1496581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root; 150c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root); 151c0d3fb39STakashi Iwai 1521da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 1536581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root; 1541da177e4SLinus Torvalds #endif 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds static void snd_remove_proc_entry(struct proc_dir_entry *parent, 1571da177e4SLinus Torvalds struct proc_dir_entry *de) 1581da177e4SLinus Torvalds { 1591da177e4SLinus Torvalds if (de) 1601da177e4SLinus Torvalds remove_proc_entry(de->name, parent); 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) 1641da177e4SLinus Torvalds { 16524c1f931STakashi Iwai struct snd_info_private_data *data; 1661da177e4SLinus Torvalds struct snd_info_entry *entry; 16773029e0fSTakashi Iwai loff_t ret = -EINVAL, size; 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds data = file->private_data; 1701da177e4SLinus Torvalds entry = data->entry; 1715b5cd553STakashi Iwai mutex_lock(&entry->access); 17273029e0fSTakashi Iwai if (entry->content == SNDRV_INFO_CONTENT_DATA && 17373029e0fSTakashi Iwai entry->c.ops->llseek) { 17473029e0fSTakashi Iwai offset = entry->c.ops->llseek(entry, 1751da177e4SLinus Torvalds data->file_private_data, 1761da177e4SLinus Torvalds file, offset, orig); 1771da177e4SLinus Torvalds goto out; 1781da177e4SLinus Torvalds } 17973029e0fSTakashi Iwai if (entry->content == SNDRV_INFO_CONTENT_DATA) 18073029e0fSTakashi Iwai size = entry->size; 18173029e0fSTakashi Iwai else 18273029e0fSTakashi Iwai size = 0; 18373029e0fSTakashi Iwai switch (orig) { 18473029e0fSTakashi Iwai case SEEK_SET: 1851da177e4SLinus Torvalds break; 18673029e0fSTakashi Iwai case SEEK_CUR: 18773029e0fSTakashi Iwai offset += file->f_pos; 18873029e0fSTakashi Iwai break; 18973029e0fSTakashi Iwai case SEEK_END: 19073029e0fSTakashi Iwai if (!size) 19173029e0fSTakashi Iwai goto out; 19273029e0fSTakashi Iwai offset += size; 19373029e0fSTakashi Iwai break; 19473029e0fSTakashi Iwai default: 19573029e0fSTakashi Iwai goto out; 1961da177e4SLinus Torvalds } 19773029e0fSTakashi Iwai if (offset < 0) 19873029e0fSTakashi Iwai goto out; 19973029e0fSTakashi Iwai if (size && offset > size) 20073029e0fSTakashi Iwai offset = size; 20173029e0fSTakashi Iwai file->f_pos = offset; 20273029e0fSTakashi Iwai ret = offset; 2031da177e4SLinus Torvalds out: 2045b5cd553STakashi Iwai mutex_unlock(&entry->access); 2051da177e4SLinus Torvalds return ret; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, 2091da177e4SLinus Torvalds size_t count, loff_t * offset) 2101da177e4SLinus Torvalds { 21124c1f931STakashi Iwai struct snd_info_private_data *data; 2121da177e4SLinus Torvalds struct snd_info_entry *entry; 21324c1f931STakashi Iwai struct snd_info_buffer *buf; 2141da177e4SLinus Torvalds size_t size = 0; 2151da177e4SLinus Torvalds loff_t pos; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds data = file->private_data; 2187eaa943cSTakashi Iwai if (snd_BUG_ON(!data)) 2197eaa943cSTakashi Iwai return -ENXIO; 2201da177e4SLinus Torvalds pos = *offset; 2211da177e4SLinus Torvalds if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 2221da177e4SLinus Torvalds return -EIO; 2231da177e4SLinus Torvalds if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 2241da177e4SLinus Torvalds return -EIO; 2251da177e4SLinus Torvalds entry = data->entry; 2261da177e4SLinus Torvalds switch (entry->content) { 2271da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_TEXT: 2281da177e4SLinus Torvalds buf = data->rbuffer; 2291da177e4SLinus Torvalds if (buf == NULL) 2301da177e4SLinus Torvalds return -EIO; 2311da177e4SLinus Torvalds if (pos >= buf->size) 2321da177e4SLinus Torvalds return 0; 2331da177e4SLinus Torvalds size = buf->size - pos; 2341da177e4SLinus Torvalds size = min(count, size); 2351da177e4SLinus Torvalds if (copy_to_user(buffer, buf->buffer + pos, size)) 2361da177e4SLinus Torvalds return -EFAULT; 2371da177e4SLinus Torvalds break; 2381da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 239d97e1b78STakashi Iwai if (pos >= entry->size) 240d97e1b78STakashi Iwai return 0; 241d97e1b78STakashi Iwai if (entry->c.ops->read) { 242d97e1b78STakashi Iwai size = entry->size - pos; 243d97e1b78STakashi Iwai size = min(count, size); 2441da177e4SLinus Torvalds size = entry->c.ops->read(entry, 2451da177e4SLinus Torvalds data->file_private_data, 246d97e1b78STakashi Iwai file, buffer, size, pos); 247d97e1b78STakashi Iwai } 2481da177e4SLinus Torvalds break; 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds if ((ssize_t) size > 0) 2511da177e4SLinus Torvalds *offset = pos + size; 2521da177e4SLinus Torvalds return size; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, 2561da177e4SLinus Torvalds size_t count, loff_t * offset) 2571da177e4SLinus Torvalds { 25824c1f931STakashi Iwai struct snd_info_private_data *data; 2591da177e4SLinus Torvalds struct snd_info_entry *entry; 26024c1f931STakashi Iwai struct snd_info_buffer *buf; 2617e4eeec8STakashi Iwai ssize_t size = 0; 2621da177e4SLinus Torvalds loff_t pos; 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds data = file->private_data; 2657eaa943cSTakashi Iwai if (snd_BUG_ON(!data)) 2667eaa943cSTakashi Iwai return -ENXIO; 2671da177e4SLinus Torvalds entry = data->entry; 2681da177e4SLinus Torvalds pos = *offset; 2691da177e4SLinus Torvalds if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 2701da177e4SLinus Torvalds return -EIO; 2711da177e4SLinus Torvalds if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 2721da177e4SLinus Torvalds return -EIO; 2731da177e4SLinus Torvalds switch (entry->content) { 2741da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_TEXT: 2751da177e4SLinus Torvalds buf = data->wbuffer; 2761da177e4SLinus Torvalds if (buf == NULL) 2771da177e4SLinus Torvalds return -EIO; 278f4a747f1SClemens Ladisch mutex_lock(&entry->access); 2797e4eeec8STakashi Iwai if (pos + count >= buf->len) { 2807e4eeec8STakashi Iwai if (resize_info_buffer(buf, pos + count)) { 2817e4eeec8STakashi Iwai mutex_unlock(&entry->access); 2821da177e4SLinus Torvalds return -ENOMEM; 2837e4eeec8STakashi Iwai } 2847e4eeec8STakashi Iwai } 2857e4eeec8STakashi Iwai if (copy_from_user(buf->buffer + pos, buffer, count)) { 2867e4eeec8STakashi Iwai mutex_unlock(&entry->access); 2871da177e4SLinus Torvalds return -EFAULT; 2887e4eeec8STakashi Iwai } 2897e4eeec8STakashi Iwai buf->size = pos + count; 2907e4eeec8STakashi Iwai mutex_unlock(&entry->access); 2917e4eeec8STakashi Iwai size = count; 2921da177e4SLinus Torvalds break; 2931da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 294d97e1b78STakashi Iwai if (entry->c.ops->write && count > 0) { 295d97e1b78STakashi Iwai size_t maxsize = entry->size - pos; 296d97e1b78STakashi Iwai count = min(count, maxsize); 2971da177e4SLinus Torvalds size = entry->c.ops->write(entry, 2981da177e4SLinus Torvalds data->file_private_data, 2991da177e4SLinus Torvalds file, buffer, count, pos); 300d97e1b78STakashi Iwai } 3011da177e4SLinus Torvalds break; 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds if ((ssize_t) size > 0) 3041da177e4SLinus Torvalds *offset = pos + size; 3051da177e4SLinus Torvalds return size; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds static int snd_info_entry_open(struct inode *inode, struct file *file) 3091da177e4SLinus Torvalds { 31024c1f931STakashi Iwai struct snd_info_entry *entry; 31124c1f931STakashi Iwai struct snd_info_private_data *data; 31224c1f931STakashi Iwai struct snd_info_buffer *buffer; 3131da177e4SLinus Torvalds struct proc_dir_entry *p; 3141da177e4SLinus Torvalds int mode, err; 3151da177e4SLinus Torvalds 3161a60d4c5SIngo Molnar mutex_lock(&info_mutex); 3171da177e4SLinus Torvalds p = PDE(inode); 31824c1f931STakashi Iwai entry = p == NULL ? NULL : (struct snd_info_entry *)p->data; 319746d4a02STakashi Iwai if (entry == NULL || ! entry->p) { 3201a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 3211da177e4SLinus Torvalds return -ENODEV; 3221da177e4SLinus Torvalds } 3231da177e4SLinus Torvalds if (!try_module_get(entry->module)) { 3241da177e4SLinus Torvalds err = -EFAULT; 3251da177e4SLinus Torvalds goto __error1; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds mode = file->f_flags & O_ACCMODE; 3281da177e4SLinus Torvalds if (mode == O_RDONLY || mode == O_RDWR) { 3297e4eeec8STakashi Iwai if ((entry->content == SNDRV_INFO_CONTENT_DATA && 3301da177e4SLinus Torvalds entry->c.ops->read == NULL)) { 3311da177e4SLinus Torvalds err = -ENODEV; 3321da177e4SLinus Torvalds goto __error; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds if (mode == O_WRONLY || mode == O_RDWR) { 3367e4eeec8STakashi Iwai if ((entry->content == SNDRV_INFO_CONTENT_DATA && 3371da177e4SLinus Torvalds entry->c.ops->write == NULL)) { 3381da177e4SLinus Torvalds err = -ENODEV; 3391da177e4SLinus Torvalds goto __error; 3401da177e4SLinus Torvalds } 3411da177e4SLinus Torvalds } 342ca2c0966STakashi Iwai data = kzalloc(sizeof(*data), GFP_KERNEL); 3431da177e4SLinus Torvalds if (data == NULL) { 3441da177e4SLinus Torvalds err = -ENOMEM; 3451da177e4SLinus Torvalds goto __error; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds data->entry = entry; 3481da177e4SLinus Torvalds switch (entry->content) { 3491da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_TEXT: 3501da177e4SLinus Torvalds if (mode == O_RDONLY || mode == O_RDWR) { 351ca2c0966STakashi Iwai buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); 3527e4eeec8STakashi Iwai if (buffer == NULL) 3537e4eeec8STakashi Iwai goto __nomem; 3541da177e4SLinus Torvalds data->rbuffer = buffer; 3557e4eeec8STakashi Iwai buffer->len = PAGE_SIZE; 3567e4eeec8STakashi Iwai buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); 3577e4eeec8STakashi Iwai if (buffer->buffer == NULL) 3587e4eeec8STakashi Iwai goto __nomem; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds if (mode == O_WRONLY || mode == O_RDWR) { 361ca2c0966STakashi Iwai buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); 3627e4eeec8STakashi Iwai if (buffer == NULL) 3637e4eeec8STakashi Iwai goto __nomem; 3641da177e4SLinus Torvalds data->wbuffer = buffer; 3657e4eeec8STakashi Iwai buffer->len = PAGE_SIZE; 3667e4eeec8STakashi Iwai buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); 3677e4eeec8STakashi Iwai if (buffer->buffer == NULL) 3687e4eeec8STakashi Iwai goto __nomem; 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds break; 3711da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: /* data */ 3721da177e4SLinus Torvalds if (entry->c.ops->open) { 3731da177e4SLinus Torvalds if ((err = entry->c.ops->open(entry, mode, 3741da177e4SLinus Torvalds &data->file_private_data)) < 0) { 3751da177e4SLinus Torvalds kfree(data); 3761da177e4SLinus Torvalds goto __error; 3771da177e4SLinus Torvalds } 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds break; 3801da177e4SLinus Torvalds } 3811da177e4SLinus Torvalds file->private_data = data; 3821a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 3831da177e4SLinus Torvalds if (entry->content == SNDRV_INFO_CONTENT_TEXT && 3841da177e4SLinus Torvalds (mode == O_RDONLY || mode == O_RDWR)) { 3851da177e4SLinus Torvalds if (entry->c.text.read) { 3861a60d4c5SIngo Molnar mutex_lock(&entry->access); 3871da177e4SLinus Torvalds entry->c.text.read(entry, data->rbuffer); 3881a60d4c5SIngo Molnar mutex_unlock(&entry->access); 3891da177e4SLinus Torvalds } 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds return 0; 3921da177e4SLinus Torvalds 3937e4eeec8STakashi Iwai __nomem: 3947e4eeec8STakashi Iwai if (data->rbuffer) { 3957e4eeec8STakashi Iwai kfree(data->rbuffer->buffer); 3967e4eeec8STakashi Iwai kfree(data->rbuffer); 3977e4eeec8STakashi Iwai } 3987e4eeec8STakashi Iwai if (data->wbuffer) { 3997e4eeec8STakashi Iwai kfree(data->wbuffer->buffer); 4007e4eeec8STakashi Iwai kfree(data->wbuffer); 4017e4eeec8STakashi Iwai } 4027e4eeec8STakashi Iwai kfree(data); 4037e4eeec8STakashi Iwai err = -ENOMEM; 4041da177e4SLinus Torvalds __error: 4051da177e4SLinus Torvalds module_put(entry->module); 4061da177e4SLinus Torvalds __error1: 4071a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 4081da177e4SLinus Torvalds return err; 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds static int snd_info_entry_release(struct inode *inode, struct file *file) 4121da177e4SLinus Torvalds { 41324c1f931STakashi Iwai struct snd_info_entry *entry; 41424c1f931STakashi Iwai struct snd_info_private_data *data; 4151da177e4SLinus Torvalds int mode; 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds mode = file->f_flags & O_ACCMODE; 4181da177e4SLinus Torvalds data = file->private_data; 4191da177e4SLinus Torvalds entry = data->entry; 4201da177e4SLinus Torvalds switch (entry->content) { 4211da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_TEXT: 4227e4eeec8STakashi Iwai if (data->rbuffer) { 4237e4eeec8STakashi Iwai kfree(data->rbuffer->buffer); 4241da177e4SLinus Torvalds kfree(data->rbuffer); 4251da177e4SLinus Torvalds } 4267e4eeec8STakashi Iwai if (data->wbuffer) { 4271da177e4SLinus Torvalds if (entry->c.text.write) { 4281da177e4SLinus Torvalds entry->c.text.write(entry, data->wbuffer); 4291da177e4SLinus Torvalds if (data->wbuffer->error) { 4301da177e4SLinus Torvalds snd_printk(KERN_WARNING "data write error to %s (%i)\n", 4311da177e4SLinus Torvalds entry->name, 4321da177e4SLinus Torvalds data->wbuffer->error); 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds } 4357e4eeec8STakashi Iwai kfree(data->wbuffer->buffer); 4361da177e4SLinus Torvalds kfree(data->wbuffer); 4371da177e4SLinus Torvalds } 4381da177e4SLinus Torvalds break; 4391da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 4401da177e4SLinus Torvalds if (entry->c.ops->release) 4411da177e4SLinus Torvalds entry->c.ops->release(entry, mode, 4421da177e4SLinus Torvalds data->file_private_data); 4431da177e4SLinus Torvalds break; 4441da177e4SLinus Torvalds } 4451da177e4SLinus Torvalds module_put(entry->module); 4461da177e4SLinus Torvalds kfree(data); 4471da177e4SLinus Torvalds return 0; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) 4511da177e4SLinus Torvalds { 45224c1f931STakashi Iwai struct snd_info_private_data *data; 4531da177e4SLinus Torvalds struct snd_info_entry *entry; 4541da177e4SLinus Torvalds unsigned int mask; 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds data = file->private_data; 4571da177e4SLinus Torvalds if (data == NULL) 4581da177e4SLinus Torvalds return 0; 4591da177e4SLinus Torvalds entry = data->entry; 4601da177e4SLinus Torvalds mask = 0; 4611da177e4SLinus Torvalds switch (entry->content) { 4621da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 4631da177e4SLinus Torvalds if (entry->c.ops->poll) 4641da177e4SLinus Torvalds return entry->c.ops->poll(entry, 4651da177e4SLinus Torvalds data->file_private_data, 4661da177e4SLinus Torvalds file, wait); 4671da177e4SLinus Torvalds if (entry->c.ops->read) 4681da177e4SLinus Torvalds mask |= POLLIN | POLLRDNORM; 4691da177e4SLinus Torvalds if (entry->c.ops->write) 4701da177e4SLinus Torvalds mask |= POLLOUT | POLLWRNORM; 4711da177e4SLinus Torvalds break; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds return mask; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds 476d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, 477d99e9889SIngo Molnar unsigned long arg) 4781da177e4SLinus Torvalds { 47924c1f931STakashi Iwai struct snd_info_private_data *data; 4801da177e4SLinus Torvalds struct snd_info_entry *entry; 4811da177e4SLinus Torvalds 4821da177e4SLinus Torvalds data = file->private_data; 4831da177e4SLinus Torvalds if (data == NULL) 4841da177e4SLinus Torvalds return 0; 4851da177e4SLinus Torvalds entry = data->entry; 4861da177e4SLinus Torvalds switch (entry->content) { 4871da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 4881da177e4SLinus Torvalds if (entry->c.ops->ioctl) 4891da177e4SLinus Torvalds return entry->c.ops->ioctl(entry, 4901da177e4SLinus Torvalds data->file_private_data, 4911da177e4SLinus Torvalds file, cmd, arg); 4921da177e4SLinus Torvalds break; 4931da177e4SLinus Torvalds } 4941da177e4SLinus Torvalds return -ENOTTY; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) 4981da177e4SLinus Torvalds { 499496ad9aaSAl Viro struct inode *inode = file_inode(file); 50024c1f931STakashi Iwai struct snd_info_private_data *data; 5011da177e4SLinus Torvalds struct snd_info_entry *entry; 5021da177e4SLinus Torvalds 5031da177e4SLinus Torvalds data = file->private_data; 5041da177e4SLinus Torvalds if (data == NULL) 5051da177e4SLinus Torvalds return 0; 5061da177e4SLinus Torvalds entry = data->entry; 5071da177e4SLinus Torvalds switch (entry->content) { 5081da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 5091da177e4SLinus Torvalds if (entry->c.ops->mmap) 5101da177e4SLinus Torvalds return entry->c.ops->mmap(entry, 5111da177e4SLinus Torvalds data->file_private_data, 5121da177e4SLinus Torvalds inode, file, vma); 5131da177e4SLinus Torvalds break; 5141da177e4SLinus Torvalds } 5151da177e4SLinus Torvalds return -ENXIO; 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 5189c2e08c5SArjan van de Ven static const struct file_operations snd_info_entry_operations = 5191da177e4SLinus Torvalds { 5201da177e4SLinus Torvalds .owner = THIS_MODULE, 5211da177e4SLinus Torvalds .llseek = snd_info_entry_llseek, 5221da177e4SLinus Torvalds .read = snd_info_entry_read, 5231da177e4SLinus Torvalds .write = snd_info_entry_write, 5241da177e4SLinus Torvalds .poll = snd_info_entry_poll, 525d99e9889SIngo Molnar .unlocked_ioctl = snd_info_entry_ioctl, 5261da177e4SLinus Torvalds .mmap = snd_info_entry_mmap, 5271da177e4SLinus Torvalds .open = snd_info_entry_open, 5281da177e4SLinus Torvalds .release = snd_info_entry_release, 5291da177e4SLinus Torvalds }; 5301da177e4SLinus Torvalds 5311da177e4SLinus Torvalds int __init snd_info_init(void) 5321da177e4SLinus Torvalds { 5331da177e4SLinus Torvalds struct proc_dir_entry *p; 5341da177e4SLinus Torvalds 535e55d92b9SAl Viro p = proc_mkdir("asound", NULL); 5361da177e4SLinus Torvalds if (p == NULL) 5371da177e4SLinus Torvalds return -ENOMEM; 5381da177e4SLinus Torvalds snd_proc_root = p; 5391da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 5401da177e4SLinus Torvalds { 54124c1f931STakashi Iwai struct snd_info_entry *entry; 5421da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL) 5431da177e4SLinus Torvalds return -ENOMEM; 5441da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 5451da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5461da177e4SLinus Torvalds snd_info_free_entry(entry); 5471da177e4SLinus Torvalds return -ENOMEM; 5481da177e4SLinus Torvalds } 5491da177e4SLinus Torvalds snd_oss_root = entry; 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds #endif 5521da177e4SLinus Torvalds #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) 5531da177e4SLinus Torvalds { 55424c1f931STakashi Iwai struct snd_info_entry *entry; 5551da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL) 5561da177e4SLinus Torvalds return -ENOMEM; 5571da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 5581da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5591da177e4SLinus Torvalds snd_info_free_entry(entry); 5601da177e4SLinus Torvalds return -ENOMEM; 5611da177e4SLinus Torvalds } 5621da177e4SLinus Torvalds snd_seq_root = entry; 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds #endif 5651da177e4SLinus Torvalds snd_info_version_init(); 5661da177e4SLinus Torvalds snd_minor_info_init(); 5671da177e4SLinus Torvalds snd_minor_info_oss_init(); 5681da177e4SLinus Torvalds snd_card_info_init(); 5691da177e4SLinus Torvalds return 0; 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds int __exit snd_info_done(void) 5731da177e4SLinus Torvalds { 5741da177e4SLinus Torvalds snd_card_info_done(); 5751da177e4SLinus Torvalds snd_minor_info_oss_done(); 5761da177e4SLinus Torvalds snd_minor_info_done(); 5771da177e4SLinus Torvalds snd_info_version_done(); 5781da177e4SLinus Torvalds if (snd_proc_root) { 5791da177e4SLinus Torvalds #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) 580746d4a02STakashi Iwai snd_info_free_entry(snd_seq_root); 5811da177e4SLinus Torvalds #endif 5821da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 583746d4a02STakashi Iwai snd_info_free_entry(snd_oss_root); 5841da177e4SLinus Torvalds #endif 585c74c120aSAlexey Dobriyan snd_remove_proc_entry(NULL, snd_proc_root); 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds return 0; 5881da177e4SLinus Torvalds } 5891da177e4SLinus Torvalds 5901da177e4SLinus Torvalds /* 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds */ 5931da177e4SLinus Torvalds 5941da177e4SLinus Torvalds 5951da177e4SLinus Torvalds /* 5961da177e4SLinus Torvalds * create a card proc file 5971da177e4SLinus Torvalds * called from init.c 5981da177e4SLinus Torvalds */ 59924c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card) 6001da177e4SLinus Torvalds { 6011da177e4SLinus Torvalds char str[8]; 60224c1f931STakashi Iwai struct snd_info_entry *entry; 6031da177e4SLinus Torvalds 6047eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 6057eaa943cSTakashi Iwai return -ENXIO; 6061da177e4SLinus Torvalds 6071da177e4SLinus Torvalds sprintf(str, "card%i", card->number); 6081da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) 6091da177e4SLinus Torvalds return -ENOMEM; 6101da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 6111da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 6121da177e4SLinus Torvalds snd_info_free_entry(entry); 6131da177e4SLinus Torvalds return -ENOMEM; 6141da177e4SLinus Torvalds } 6151da177e4SLinus Torvalds card->proc_root = entry; 6161da177e4SLinus Torvalds return 0; 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds /* 6201da177e4SLinus Torvalds * register the card proc file 6211da177e4SLinus Torvalds * called from init.c 6221da177e4SLinus Torvalds */ 62324c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card) 6241da177e4SLinus Torvalds { 6251da177e4SLinus Torvalds struct proc_dir_entry *p; 6261da177e4SLinus Torvalds 6277eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 6287eaa943cSTakashi Iwai return -ENXIO; 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds if (!strcmp(card->id, card->proc_root->name)) 6311da177e4SLinus Torvalds return 0; 6321da177e4SLinus Torvalds 6331da177e4SLinus Torvalds p = proc_symlink(card->id, snd_proc_root, card->proc_root->name); 6341da177e4SLinus Torvalds if (p == NULL) 6351da177e4SLinus Torvalds return -ENOMEM; 6361da177e4SLinus Torvalds card->proc_root_link = p; 6371da177e4SLinus Torvalds return 0; 6381da177e4SLinus Torvalds } 6391da177e4SLinus Torvalds 6401da177e4SLinus Torvalds /* 641c2eb9c4eSJaroslav Kysela * called on card->id change 642c2eb9c4eSJaroslav Kysela */ 643c2eb9c4eSJaroslav Kysela void snd_info_card_id_change(struct snd_card *card) 644c2eb9c4eSJaroslav Kysela { 645c2eb9c4eSJaroslav Kysela mutex_lock(&info_mutex); 646c2eb9c4eSJaroslav Kysela if (card->proc_root_link) { 647c2eb9c4eSJaroslav Kysela snd_remove_proc_entry(snd_proc_root, card->proc_root_link); 648c2eb9c4eSJaroslav Kysela card->proc_root_link = NULL; 649c2eb9c4eSJaroslav Kysela } 650c2eb9c4eSJaroslav Kysela if (strcmp(card->id, card->proc_root->name)) 651c2eb9c4eSJaroslav Kysela card->proc_root_link = proc_symlink(card->id, 652c2eb9c4eSJaroslav Kysela snd_proc_root, 653c2eb9c4eSJaroslav Kysela card->proc_root->name); 654c2eb9c4eSJaroslav Kysela mutex_unlock(&info_mutex); 655c2eb9c4eSJaroslav Kysela } 656c2eb9c4eSJaroslav Kysela 657c2eb9c4eSJaroslav Kysela /* 6581da177e4SLinus Torvalds * de-register the card proc file 6591da177e4SLinus Torvalds * called from init.c 6601da177e4SLinus Torvalds */ 661746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card) 6621da177e4SLinus Torvalds { 6637eaa943cSTakashi Iwai if (!card) 6647eaa943cSTakashi Iwai return; 665746d4a02STakashi Iwai mutex_lock(&info_mutex); 6661da177e4SLinus Torvalds if (card->proc_root_link) { 6671da177e4SLinus Torvalds snd_remove_proc_entry(snd_proc_root, card->proc_root_link); 6681da177e4SLinus Torvalds card->proc_root_link = NULL; 6691da177e4SLinus Torvalds } 670746d4a02STakashi Iwai if (card->proc_root) 671746d4a02STakashi Iwai snd_info_disconnect(card->proc_root); 672746d4a02STakashi Iwai mutex_unlock(&info_mutex); 6731da177e4SLinus Torvalds } 674746d4a02STakashi Iwai 675746d4a02STakashi Iwai /* 676746d4a02STakashi Iwai * release the card proc file resources 677746d4a02STakashi Iwai * called from init.c 678746d4a02STakashi Iwai */ 679746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card) 680746d4a02STakashi Iwai { 6817eaa943cSTakashi Iwai if (!card) 6827eaa943cSTakashi Iwai return 0; 683746d4a02STakashi Iwai snd_info_free_entry(card->proc_root); 684746d4a02STakashi Iwai card->proc_root = NULL; 6851da177e4SLinus Torvalds return 0; 6861da177e4SLinus Torvalds } 6871da177e4SLinus Torvalds 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds /** 6901da177e4SLinus Torvalds * snd_info_get_line - read one line from the procfs buffer 6911da177e4SLinus Torvalds * @buffer: the procfs buffer 6921da177e4SLinus Torvalds * @line: the buffer to store 6931da177e4SLinus Torvalds * @len: the max. buffer size - 1 6941da177e4SLinus Torvalds * 6951da177e4SLinus Torvalds * Reads one line from the buffer and stores the string. 6961da177e4SLinus Torvalds * 6971da177e4SLinus Torvalds * Returns zero if successful, or 1 if error or EOF. 6981da177e4SLinus Torvalds */ 69924c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) 7001da177e4SLinus Torvalds { 7011da177e4SLinus Torvalds int c = -1; 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds if (len <= 0 || buffer->stop || buffer->error) 7041da177e4SLinus Torvalds return 1; 7051da177e4SLinus Torvalds while (--len > 0) { 7067e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++]; 7071da177e4SLinus Torvalds if (c == '\n') { 7087e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) 7091da177e4SLinus Torvalds buffer->stop = 1; 7101da177e4SLinus Torvalds break; 7111da177e4SLinus Torvalds } 7121da177e4SLinus Torvalds *line++ = c; 7137e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) { 7141da177e4SLinus Torvalds buffer->stop = 1; 7151da177e4SLinus Torvalds break; 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds while (c != '\n' && !buffer->stop) { 7197e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++]; 7207e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) 7211da177e4SLinus Torvalds buffer->stop = 1; 7221da177e4SLinus Torvalds } 7231da177e4SLinus Torvalds *line = '\0'; 7241da177e4SLinus Torvalds return 0; 7251da177e4SLinus Torvalds } 7261da177e4SLinus Torvalds 727c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line); 728c0d3fb39STakashi Iwai 7291da177e4SLinus Torvalds /** 730856def8aSHenrik Kretzschmar * snd_info_get_str - parse a string token 7311da177e4SLinus Torvalds * @dest: the buffer to store the string token 7321da177e4SLinus Torvalds * @src: the original string 7331da177e4SLinus Torvalds * @len: the max. length of token - 1 7341da177e4SLinus Torvalds * 7351da177e4SLinus Torvalds * Parses the original string and copy a token to the given 7361da177e4SLinus Torvalds * string buffer. 7371da177e4SLinus Torvalds * 7381da177e4SLinus Torvalds * Returns the updated pointer of the original string so that 7391da177e4SLinus Torvalds * it can be used for the next call. 7401da177e4SLinus Torvalds */ 7414f7454a9STakashi Iwai const char *snd_info_get_str(char *dest, const char *src, int len) 7421da177e4SLinus Torvalds { 7431da177e4SLinus Torvalds int c; 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 7461da177e4SLinus Torvalds src++; 7471da177e4SLinus Torvalds if (*src == '"' || *src == '\'') { 7481da177e4SLinus Torvalds c = *src++; 7491da177e4SLinus Torvalds while (--len > 0 && *src && *src != c) { 7501da177e4SLinus Torvalds *dest++ = *src++; 7511da177e4SLinus Torvalds } 7521da177e4SLinus Torvalds if (*src == c) 7531da177e4SLinus Torvalds src++; 7541da177e4SLinus Torvalds } else { 7551da177e4SLinus Torvalds while (--len > 0 && *src && *src != ' ' && *src != '\t') { 7561da177e4SLinus Torvalds *dest++ = *src++; 7571da177e4SLinus Torvalds } 7581da177e4SLinus Torvalds } 7591da177e4SLinus Torvalds *dest = 0; 7601da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 7611da177e4SLinus Torvalds src++; 7621da177e4SLinus Torvalds return src; 7631da177e4SLinus Torvalds } 7641da177e4SLinus Torvalds 765c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str); 766c0d3fb39STakashi Iwai 7671da177e4SLinus Torvalds /** 7681da177e4SLinus Torvalds * snd_info_create_entry - create an info entry 7691da177e4SLinus Torvalds * @name: the proc file name 7701da177e4SLinus Torvalds * 7711da177e4SLinus Torvalds * Creates an info entry with the given file name and initializes as 7721da177e4SLinus Torvalds * the default state. 7731da177e4SLinus Torvalds * 7741da177e4SLinus Torvalds * Usually called from other functions such as 7751da177e4SLinus Torvalds * snd_info_create_card_entry(). 7761da177e4SLinus Torvalds * 7771da177e4SLinus Torvalds * Returns the pointer of the new instance, or NULL on failure. 7781da177e4SLinus Torvalds */ 77924c1f931STakashi Iwai static struct snd_info_entry *snd_info_create_entry(const char *name) 7801da177e4SLinus Torvalds { 78124c1f931STakashi Iwai struct snd_info_entry *entry; 782ca2c0966STakashi Iwai entry = kzalloc(sizeof(*entry), GFP_KERNEL); 7831da177e4SLinus Torvalds if (entry == NULL) 7841da177e4SLinus Torvalds return NULL; 785543537bdSPaulo Marques entry->name = kstrdup(name, GFP_KERNEL); 7861da177e4SLinus Torvalds if (entry->name == NULL) { 7871da177e4SLinus Torvalds kfree(entry); 7881da177e4SLinus Torvalds return NULL; 7891da177e4SLinus Torvalds } 7901da177e4SLinus Torvalds entry->mode = S_IFREG | S_IRUGO; 7911da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_TEXT; 7921a60d4c5SIngo Molnar mutex_init(&entry->access); 793746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->children); 794746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->list); 7951da177e4SLinus Torvalds return entry; 7961da177e4SLinus Torvalds } 7971da177e4SLinus Torvalds 7981da177e4SLinus Torvalds /** 7991da177e4SLinus Torvalds * snd_info_create_module_entry - create an info entry for the given module 8001da177e4SLinus Torvalds * @module: the module pointer 8011da177e4SLinus Torvalds * @name: the file name 8021da177e4SLinus Torvalds * @parent: the parent directory 8031da177e4SLinus Torvalds * 8041da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given module. 8051da177e4SLinus Torvalds * 8061da177e4SLinus Torvalds * Returns the pointer of the new instance, or NULL on failure. 8071da177e4SLinus Torvalds */ 80824c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module, 8091da177e4SLinus Torvalds const char *name, 81024c1f931STakashi Iwai struct snd_info_entry *parent) 8111da177e4SLinus Torvalds { 81224c1f931STakashi Iwai struct snd_info_entry *entry = snd_info_create_entry(name); 8131da177e4SLinus Torvalds if (entry) { 8141da177e4SLinus Torvalds entry->module = module; 8151da177e4SLinus Torvalds entry->parent = parent; 8161da177e4SLinus Torvalds } 8171da177e4SLinus Torvalds return entry; 8181da177e4SLinus Torvalds } 8191da177e4SLinus Torvalds 820c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry); 821c0d3fb39STakashi Iwai 8221da177e4SLinus Torvalds /** 8231da177e4SLinus Torvalds * snd_info_create_card_entry - create an info entry for the given card 8241da177e4SLinus Torvalds * @card: the card instance 8251da177e4SLinus Torvalds * @name: the file name 8261da177e4SLinus Torvalds * @parent: the parent directory 8271da177e4SLinus Torvalds * 8281da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card. 8291da177e4SLinus Torvalds * 8301da177e4SLinus Torvalds * Returns the pointer of the new instance, or NULL on failure. 8311da177e4SLinus Torvalds */ 83224c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, 8331da177e4SLinus Torvalds const char *name, 83424c1f931STakashi Iwai struct snd_info_entry * parent) 8351da177e4SLinus Torvalds { 83624c1f931STakashi Iwai struct snd_info_entry *entry = snd_info_create_entry(name); 8371da177e4SLinus Torvalds if (entry) { 8381da177e4SLinus Torvalds entry->module = card->module; 8391da177e4SLinus Torvalds entry->card = card; 8401da177e4SLinus Torvalds entry->parent = parent; 8411da177e4SLinus Torvalds } 8421da177e4SLinus Torvalds return entry; 8431da177e4SLinus Torvalds } 8441da177e4SLinus Torvalds 845c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry); 846c0d3fb39STakashi Iwai 847746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry) 848746d4a02STakashi Iwai { 849746d4a02STakashi Iwai struct list_head *p, *n; 850746d4a02STakashi Iwai struct proc_dir_entry *root; 851746d4a02STakashi Iwai 852746d4a02STakashi Iwai list_for_each_safe(p, n, &entry->children) { 853746d4a02STakashi Iwai snd_info_disconnect(list_entry(p, struct snd_info_entry, list)); 854746d4a02STakashi Iwai } 855746d4a02STakashi Iwai 856746d4a02STakashi Iwai if (! entry->p) 857746d4a02STakashi Iwai return; 858746d4a02STakashi Iwai list_del_init(&entry->list); 859746d4a02STakashi Iwai root = entry->parent == NULL ? snd_proc_root : entry->parent->p; 8607eaa943cSTakashi Iwai snd_BUG_ON(!root); 861746d4a02STakashi Iwai snd_remove_proc_entry(root, entry->p); 862746d4a02STakashi Iwai entry->p = NULL; 863746d4a02STakashi Iwai } 864746d4a02STakashi Iwai 86524c1f931STakashi Iwai static int snd_info_dev_free_entry(struct snd_device *device) 8661da177e4SLinus Torvalds { 86724c1f931STakashi Iwai struct snd_info_entry *entry = device->device_data; 8681da177e4SLinus Torvalds snd_info_free_entry(entry); 8691da177e4SLinus Torvalds return 0; 8701da177e4SLinus Torvalds } 8711da177e4SLinus Torvalds 87224c1f931STakashi Iwai static int snd_info_dev_register_entry(struct snd_device *device) 8731da177e4SLinus Torvalds { 87424c1f931STakashi Iwai struct snd_info_entry *entry = device->device_data; 8751da177e4SLinus Torvalds return snd_info_register(entry); 8761da177e4SLinus Torvalds } 8771da177e4SLinus Torvalds 8781da177e4SLinus Torvalds /** 8791da177e4SLinus Torvalds * snd_card_proc_new - create an info entry for the given card 8801da177e4SLinus Torvalds * @card: the card instance 8811da177e4SLinus Torvalds * @name: the file name 8821da177e4SLinus Torvalds * @entryp: the pointer to store the new info entry 8831da177e4SLinus Torvalds * 8841da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card. 8851da177e4SLinus Torvalds * Unlike snd_info_create_card_entry(), this function registers the 8861da177e4SLinus Torvalds * info entry as an ALSA device component, so that it can be 8871da177e4SLinus Torvalds * unregistered/released without explicit call. 8881da177e4SLinus Torvalds * Also, you don't have to register this entry via snd_info_register(), 8891da177e4SLinus Torvalds * since this will be registered by snd_card_register() automatically. 8901da177e4SLinus Torvalds * 8911da177e4SLinus Torvalds * The parent is assumed as card->proc_root. 8921da177e4SLinus Torvalds * 8931da177e4SLinus Torvalds * For releasing this entry, use snd_device_free() instead of 8941da177e4SLinus Torvalds * snd_info_free_entry(). 8951da177e4SLinus Torvalds * 8961da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 8971da177e4SLinus Torvalds */ 89824c1f931STakashi Iwai int snd_card_proc_new(struct snd_card *card, const char *name, 89924c1f931STakashi Iwai struct snd_info_entry **entryp) 9001da177e4SLinus Torvalds { 90124c1f931STakashi Iwai static struct snd_device_ops ops = { 9021da177e4SLinus Torvalds .dev_free = snd_info_dev_free_entry, 9031da177e4SLinus Torvalds .dev_register = snd_info_dev_register_entry, 904746d4a02STakashi Iwai /* disconnect is done via snd_info_card_disconnect() */ 9051da177e4SLinus Torvalds }; 90624c1f931STakashi Iwai struct snd_info_entry *entry; 9071da177e4SLinus Torvalds int err; 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds entry = snd_info_create_card_entry(card, name, card->proc_root); 9101da177e4SLinus Torvalds if (! entry) 9111da177e4SLinus Torvalds return -ENOMEM; 9121da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) { 9131da177e4SLinus Torvalds snd_info_free_entry(entry); 9141da177e4SLinus Torvalds return err; 9151da177e4SLinus Torvalds } 9161da177e4SLinus Torvalds if (entryp) 9171da177e4SLinus Torvalds *entryp = entry; 9181da177e4SLinus Torvalds return 0; 9191da177e4SLinus Torvalds } 9201da177e4SLinus Torvalds 921c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_proc_new); 922c0d3fb39STakashi Iwai 9231da177e4SLinus Torvalds /** 9241da177e4SLinus Torvalds * snd_info_free_entry - release the info entry 9251da177e4SLinus Torvalds * @entry: the info entry 9261da177e4SLinus Torvalds * 9271da177e4SLinus Torvalds * Releases the info entry. Don't call this after registered. 9281da177e4SLinus Torvalds */ 92924c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry) 9301da177e4SLinus Torvalds { 9311da177e4SLinus Torvalds if (entry == NULL) 9321da177e4SLinus Torvalds return; 933746d4a02STakashi Iwai if (entry->p) { 934746d4a02STakashi Iwai mutex_lock(&info_mutex); 935746d4a02STakashi Iwai snd_info_disconnect(entry); 936746d4a02STakashi Iwai mutex_unlock(&info_mutex); 937746d4a02STakashi Iwai } 9381da177e4SLinus Torvalds kfree(entry->name); 9391da177e4SLinus Torvalds if (entry->private_free) 9401da177e4SLinus Torvalds entry->private_free(entry); 9411da177e4SLinus Torvalds kfree(entry); 9421da177e4SLinus Torvalds } 9431da177e4SLinus Torvalds 944c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry); 945c0d3fb39STakashi Iwai 9461da177e4SLinus Torvalds /** 9471da177e4SLinus Torvalds * snd_info_register - register the info entry 9481da177e4SLinus Torvalds * @entry: the info entry 9491da177e4SLinus Torvalds * 9501da177e4SLinus Torvalds * Registers the proc info entry. 9511da177e4SLinus Torvalds * 9521da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 9531da177e4SLinus Torvalds */ 95424c1f931STakashi Iwai int snd_info_register(struct snd_info_entry * entry) 9551da177e4SLinus Torvalds { 9561da177e4SLinus Torvalds struct proc_dir_entry *root, *p = NULL; 9571da177e4SLinus Torvalds 9587eaa943cSTakashi Iwai if (snd_BUG_ON(!entry)) 9597eaa943cSTakashi Iwai return -ENXIO; 9601da177e4SLinus Torvalds root = entry->parent == NULL ? snd_proc_root : entry->parent->p; 9611a60d4c5SIngo Molnar mutex_lock(&info_mutex); 962aee0c612SAl Viro if (S_ISDIR(entry->mode)) { 963aee0c612SAl Viro p = proc_mkdir_mode(entry->name, entry->mode, root); 9641da177e4SLinus Torvalds if (!p) { 9651a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 9661da177e4SLinus Torvalds return -ENOMEM; 9671da177e4SLinus Torvalds } 968aee0c612SAl Viro } else { 969aee0c612SAl Viro p = proc_create_data(entry->name, entry->mode, root, 970aee0c612SAl Viro &snd_info_entry_operations, entry); 971aee0c612SAl Viro if (!p) { 972aee0c612SAl Viro mutex_unlock(&info_mutex); 973aee0c612SAl Viro return -ENOMEM; 974aee0c612SAl Viro } 9751da177e4SLinus Torvalds p->size = entry->size; 976aee0c612SAl Viro } 9771da177e4SLinus Torvalds entry->p = p; 978746d4a02STakashi Iwai if (entry->parent) 979746d4a02STakashi Iwai list_add_tail(&entry->list, &entry->parent->children); 9801a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 9811da177e4SLinus Torvalds return 0; 9821da177e4SLinus Torvalds } 9831da177e4SLinus Torvalds 984c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register); 985c0d3fb39STakashi Iwai 9861da177e4SLinus Torvalds /* 9871da177e4SLinus Torvalds 9881da177e4SLinus Torvalds */ 9891da177e4SLinus Torvalds 9906581f4e7STakashi Iwai static struct snd_info_entry *snd_info_version_entry; 9911da177e4SLinus Torvalds 99224c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 9931da177e4SLinus Torvalds { 9941da177e4SLinus Torvalds snd_iprintf(buffer, 99542662748SJaroslav Kysela "Advanced Linux Sound Architecture Driver Version k%s.\n", 99642662748SJaroslav Kysela init_utsname()->release); 9971da177e4SLinus Torvalds } 9981da177e4SLinus Torvalds 9991da177e4SLinus Torvalds static int __init snd_info_version_init(void) 10001da177e4SLinus Torvalds { 100124c1f931STakashi Iwai struct snd_info_entry *entry; 10021da177e4SLinus Torvalds 10031da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); 10041da177e4SLinus Torvalds if (entry == NULL) 10051da177e4SLinus Torvalds return -ENOMEM; 10061da177e4SLinus Torvalds entry->c.text.read = snd_info_version_read; 10071da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 10081da177e4SLinus Torvalds snd_info_free_entry(entry); 10091da177e4SLinus Torvalds return -ENOMEM; 10101da177e4SLinus Torvalds } 10111da177e4SLinus Torvalds snd_info_version_entry = entry; 10121da177e4SLinus Torvalds return 0; 10131da177e4SLinus Torvalds } 10141da177e4SLinus Torvalds 10151da177e4SLinus Torvalds static int __exit snd_info_version_done(void) 10161da177e4SLinus Torvalds { 1017746d4a02STakashi Iwai snd_info_free_entry(snd_info_version_entry); 10181da177e4SLinus Torvalds return 0; 10191da177e4SLinus Torvalds } 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 1022