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 <sound/driver.h> 231da177e4SLinus Torvalds #include <linux/init.h> 241da177e4SLinus Torvalds #include <linux/time.h> 251da177e4SLinus Torvalds #include <linux/smp_lock.h> 26543537bdSPaulo Marques #include <linux/string.h> 271da177e4SLinus Torvalds #include <sound/core.h> 281da177e4SLinus Torvalds #include <sound/minors.h> 291da177e4SLinus Torvalds #include <sound/info.h> 301da177e4SLinus Torvalds #include <sound/version.h> 311da177e4SLinus Torvalds #include <linux/proc_fs.h> 321a60d4c5SIngo Molnar #include <linux/mutex.h> 331da177e4SLinus Torvalds #include <stdarg.h> 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds /* 361da177e4SLinus Torvalds * 371da177e4SLinus Torvalds */ 381da177e4SLinus Torvalds 39e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 40e28563ccSTakashi Iwai 411da177e4SLinus Torvalds int snd_info_check_reserved_words(const char *str) 421da177e4SLinus Torvalds { 431da177e4SLinus Torvalds static char *reserved[] = 441da177e4SLinus Torvalds { 451da177e4SLinus Torvalds "version", 461da177e4SLinus Torvalds "meminfo", 471da177e4SLinus Torvalds "memdebug", 481da177e4SLinus Torvalds "detect", 491da177e4SLinus Torvalds "devices", 501da177e4SLinus Torvalds "oss", 511da177e4SLinus Torvalds "cards", 521da177e4SLinus Torvalds "timers", 531da177e4SLinus Torvalds "synth", 541da177e4SLinus Torvalds "pcm", 551da177e4SLinus Torvalds "seq", 561da177e4SLinus Torvalds NULL 571da177e4SLinus Torvalds }; 581da177e4SLinus Torvalds char **xstr = reserved; 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds while (*xstr) { 611da177e4SLinus Torvalds if (!strcmp(*xstr, str)) 621da177e4SLinus Torvalds return 0; 631da177e4SLinus Torvalds xstr++; 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds if (!strncmp(str, "card", 4)) 661da177e4SLinus Torvalds return 0; 671da177e4SLinus Torvalds return 1; 681da177e4SLinus Torvalds } 691da177e4SLinus Torvalds 701a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex); 711da177e4SLinus Torvalds 7224c1f931STakashi Iwai struct snd_info_private_data { 7324c1f931STakashi Iwai struct snd_info_buffer *rbuffer; 7424c1f931STakashi Iwai struct snd_info_buffer *wbuffer; 7524c1f931STakashi Iwai struct snd_info_entry *entry; 761da177e4SLinus Torvalds void *file_private_data; 7724c1f931STakashi Iwai }; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds static int snd_info_version_init(void); 801da177e4SLinus Torvalds static int snd_info_version_done(void); 81746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry); 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds 847e4eeec8STakashi Iwai /* resize the proc r/w buffer */ 857e4eeec8STakashi Iwai static int resize_info_buffer(struct snd_info_buffer *buffer, 867e4eeec8STakashi Iwai unsigned int nsize) 877e4eeec8STakashi Iwai { 887e4eeec8STakashi Iwai char *nbuf; 897e4eeec8STakashi Iwai 907e4eeec8STakashi Iwai nsize = PAGE_ALIGN(nsize); 917e4eeec8STakashi Iwai nbuf = kmalloc(nsize, GFP_KERNEL); 927e4eeec8STakashi Iwai if (! nbuf) 937e4eeec8STakashi Iwai return -ENOMEM; 947e4eeec8STakashi Iwai 957e4eeec8STakashi Iwai memcpy(nbuf, buffer->buffer, buffer->len); 967e4eeec8STakashi Iwai kfree(buffer->buffer); 977e4eeec8STakashi Iwai buffer->buffer = nbuf; 987e4eeec8STakashi Iwai buffer->len = nsize; 997e4eeec8STakashi Iwai return 0; 1007e4eeec8STakashi Iwai } 1017e4eeec8STakashi Iwai 1021da177e4SLinus Torvalds /** 1031da177e4SLinus Torvalds * snd_iprintf - printf on the procfs buffer 1041da177e4SLinus Torvalds * @buffer: the procfs buffer 1051da177e4SLinus Torvalds * @fmt: the printf format 1061da177e4SLinus Torvalds * 1071da177e4SLinus Torvalds * Outputs the string on the procfs buffer just like printf(). 1081da177e4SLinus Torvalds * 1091da177e4SLinus Torvalds * Returns the size of output string. 1101da177e4SLinus Torvalds */ 11124c1f931STakashi Iwai int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...) 1121da177e4SLinus Torvalds { 1131da177e4SLinus Torvalds va_list args; 1141da177e4SLinus Torvalds int len, res; 1157e4eeec8STakashi Iwai int err = 0; 1161da177e4SLinus Torvalds 117f001c3acSTakashi Iwai might_sleep(); 1181da177e4SLinus Torvalds if (buffer->stop || buffer->error) 1191da177e4SLinus Torvalds return 0; 1201da177e4SLinus Torvalds len = buffer->len - buffer->size; 1211da177e4SLinus Torvalds va_start(args, fmt); 1227e4eeec8STakashi Iwai for (;;) { 123dbedca39STakashi Iwai va_list ap; 124dbedca39STakashi Iwai va_copy(ap, args); 125dbedca39STakashi Iwai res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap); 126dbedca39STakashi Iwai va_end(ap); 1277e4eeec8STakashi Iwai if (res < len) 1287e4eeec8STakashi Iwai break; 1297e4eeec8STakashi Iwai err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE); 1307e4eeec8STakashi Iwai if (err < 0) 1317e4eeec8STakashi Iwai break; 1327e4eeec8STakashi Iwai len = buffer->len - buffer->size; 1331da177e4SLinus Torvalds } 1347e4eeec8STakashi Iwai va_end(args); 1357e4eeec8STakashi Iwai 1367e4eeec8STakashi Iwai if (err < 0) 1377e4eeec8STakashi Iwai return err; 1381da177e4SLinus Torvalds buffer->curr += res; 1391da177e4SLinus Torvalds buffer->size += res; 1401da177e4SLinus Torvalds return res; 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 143c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_iprintf); 144c0d3fb39STakashi Iwai 1451da177e4SLinus Torvalds /* 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds */ 1481da177e4SLinus Torvalds 1496581f4e7STakashi Iwai static struct proc_dir_entry *snd_proc_root; 1506581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root; 151c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root); 152c0d3fb39STakashi Iwai 1531da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 1546581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root; 1551da177e4SLinus Torvalds #endif 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds static inline void snd_info_entry_prepare(struct proc_dir_entry *de) 1581da177e4SLinus Torvalds { 1591da177e4SLinus Torvalds de->owner = THIS_MODULE; 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds static void snd_remove_proc_entry(struct proc_dir_entry *parent, 1631da177e4SLinus Torvalds struct proc_dir_entry *de) 1641da177e4SLinus Torvalds { 1651da177e4SLinus Torvalds if (de) 1661da177e4SLinus Torvalds remove_proc_entry(de->name, parent); 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) 1701da177e4SLinus Torvalds { 17124c1f931STakashi Iwai struct snd_info_private_data *data; 1721da177e4SLinus Torvalds struct snd_info_entry *entry; 1731da177e4SLinus Torvalds loff_t ret; 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds data = file->private_data; 1761da177e4SLinus Torvalds entry = data->entry; 1771da177e4SLinus Torvalds lock_kernel(); 1781da177e4SLinus Torvalds switch (entry->content) { 1791da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_TEXT: 1801da177e4SLinus Torvalds switch (orig) { 181e6f8f108SJosef 'Jeff' Sipek case SEEK_SET: 1821da177e4SLinus Torvalds file->f_pos = offset; 1831da177e4SLinus Torvalds ret = file->f_pos; 1841da177e4SLinus Torvalds goto out; 185e6f8f108SJosef 'Jeff' Sipek case SEEK_CUR: 1861da177e4SLinus Torvalds file->f_pos += offset; 1871da177e4SLinus Torvalds ret = file->f_pos; 1881da177e4SLinus Torvalds goto out; 189e6f8f108SJosef 'Jeff' Sipek case SEEK_END: 1901da177e4SLinus Torvalds default: 1911da177e4SLinus Torvalds ret = -EINVAL; 1921da177e4SLinus Torvalds goto out; 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds break; 1951da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 1961da177e4SLinus Torvalds if (entry->c.ops->llseek) { 1971da177e4SLinus Torvalds ret = entry->c.ops->llseek(entry, 1981da177e4SLinus Torvalds data->file_private_data, 1991da177e4SLinus Torvalds file, offset, orig); 2001da177e4SLinus Torvalds goto out; 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds break; 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds ret = -ENXIO; 2051da177e4SLinus Torvalds out: 2061da177e4SLinus Torvalds unlock_kernel(); 2071da177e4SLinus Torvalds return ret; 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, 2111da177e4SLinus Torvalds size_t count, loff_t * offset) 2121da177e4SLinus Torvalds { 21324c1f931STakashi Iwai struct snd_info_private_data *data; 2141da177e4SLinus Torvalds struct snd_info_entry *entry; 21524c1f931STakashi Iwai struct snd_info_buffer *buf; 2161da177e4SLinus Torvalds size_t size = 0; 2171da177e4SLinus Torvalds loff_t pos; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds data = file->private_data; 2201da177e4SLinus Torvalds snd_assert(data != NULL, return -ENXIO); 2211da177e4SLinus Torvalds pos = *offset; 2221da177e4SLinus Torvalds if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 2231da177e4SLinus Torvalds return -EIO; 2241da177e4SLinus Torvalds if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 2251da177e4SLinus Torvalds return -EIO; 2261da177e4SLinus Torvalds entry = data->entry; 2271da177e4SLinus Torvalds switch (entry->content) { 2281da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_TEXT: 2291da177e4SLinus Torvalds buf = data->rbuffer; 2301da177e4SLinus Torvalds if (buf == NULL) 2311da177e4SLinus Torvalds return -EIO; 2321da177e4SLinus Torvalds if (pos >= buf->size) 2331da177e4SLinus Torvalds return 0; 2341da177e4SLinus Torvalds size = buf->size - pos; 2351da177e4SLinus Torvalds size = min(count, size); 2361da177e4SLinus Torvalds if (copy_to_user(buffer, buf->buffer + pos, size)) 2371da177e4SLinus Torvalds return -EFAULT; 2381da177e4SLinus Torvalds break; 2391da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 2401da177e4SLinus Torvalds if (entry->c.ops->read) 2411da177e4SLinus Torvalds size = entry->c.ops->read(entry, 2421da177e4SLinus Torvalds data->file_private_data, 2431da177e4SLinus Torvalds file, buffer, count, pos); 2441da177e4SLinus Torvalds break; 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds if ((ssize_t) size > 0) 2471da177e4SLinus Torvalds *offset = pos + size; 2481da177e4SLinus Torvalds return size; 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, 2521da177e4SLinus Torvalds size_t count, loff_t * offset) 2531da177e4SLinus Torvalds { 25424c1f931STakashi Iwai struct snd_info_private_data *data; 2551da177e4SLinus Torvalds struct snd_info_entry *entry; 25624c1f931STakashi Iwai struct snd_info_buffer *buf; 2577e4eeec8STakashi Iwai ssize_t size = 0; 2581da177e4SLinus Torvalds loff_t pos; 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds data = file->private_data; 2611da177e4SLinus Torvalds snd_assert(data != NULL, return -ENXIO); 2621da177e4SLinus Torvalds entry = data->entry; 2631da177e4SLinus Torvalds pos = *offset; 2641da177e4SLinus Torvalds if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 2651da177e4SLinus Torvalds return -EIO; 2661da177e4SLinus Torvalds if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 2671da177e4SLinus Torvalds return -EIO; 2681da177e4SLinus Torvalds switch (entry->content) { 2691da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_TEXT: 2701da177e4SLinus Torvalds buf = data->wbuffer; 2711da177e4SLinus Torvalds if (buf == NULL) 2721da177e4SLinus Torvalds return -EIO; 273f4a747f1SClemens Ladisch mutex_lock(&entry->access); 2747e4eeec8STakashi Iwai if (pos + count >= buf->len) { 2757e4eeec8STakashi Iwai if (resize_info_buffer(buf, pos + count)) { 2767e4eeec8STakashi Iwai mutex_unlock(&entry->access); 2771da177e4SLinus Torvalds return -ENOMEM; 2787e4eeec8STakashi Iwai } 2797e4eeec8STakashi Iwai } 2807e4eeec8STakashi Iwai if (copy_from_user(buf->buffer + pos, buffer, count)) { 2817e4eeec8STakashi Iwai mutex_unlock(&entry->access); 2821da177e4SLinus Torvalds return -EFAULT; 2837e4eeec8STakashi Iwai } 2847e4eeec8STakashi Iwai buf->size = pos + count; 2857e4eeec8STakashi Iwai mutex_unlock(&entry->access); 2867e4eeec8STakashi Iwai size = count; 2871da177e4SLinus Torvalds break; 2881da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 2891da177e4SLinus Torvalds if (entry->c.ops->write) 2901da177e4SLinus Torvalds size = entry->c.ops->write(entry, 2911da177e4SLinus Torvalds data->file_private_data, 2921da177e4SLinus Torvalds file, buffer, count, pos); 2931da177e4SLinus Torvalds break; 2941da177e4SLinus Torvalds } 2951da177e4SLinus Torvalds if ((ssize_t) size > 0) 2961da177e4SLinus Torvalds *offset = pos + size; 2971da177e4SLinus Torvalds return size; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds static int snd_info_entry_open(struct inode *inode, struct file *file) 3011da177e4SLinus Torvalds { 30224c1f931STakashi Iwai struct snd_info_entry *entry; 30324c1f931STakashi Iwai struct snd_info_private_data *data; 30424c1f931STakashi Iwai struct snd_info_buffer *buffer; 3051da177e4SLinus Torvalds struct proc_dir_entry *p; 3061da177e4SLinus Torvalds int mode, err; 3071da177e4SLinus Torvalds 3081a60d4c5SIngo Molnar mutex_lock(&info_mutex); 3091da177e4SLinus Torvalds p = PDE(inode); 31024c1f931STakashi Iwai entry = p == NULL ? NULL : (struct snd_info_entry *)p->data; 311746d4a02STakashi Iwai if (entry == NULL || ! entry->p) { 3121a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 3131da177e4SLinus Torvalds return -ENODEV; 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds if (!try_module_get(entry->module)) { 3161da177e4SLinus Torvalds err = -EFAULT; 3171da177e4SLinus Torvalds goto __error1; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds mode = file->f_flags & O_ACCMODE; 3201da177e4SLinus Torvalds if (mode == O_RDONLY || mode == O_RDWR) { 3217e4eeec8STakashi Iwai if ((entry->content == SNDRV_INFO_CONTENT_DATA && 3221da177e4SLinus Torvalds entry->c.ops->read == NULL)) { 3231da177e4SLinus Torvalds err = -ENODEV; 3241da177e4SLinus Torvalds goto __error; 3251da177e4SLinus Torvalds } 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds if (mode == O_WRONLY || mode == O_RDWR) { 3287e4eeec8STakashi Iwai if ((entry->content == SNDRV_INFO_CONTENT_DATA && 3291da177e4SLinus Torvalds entry->c.ops->write == NULL)) { 3301da177e4SLinus Torvalds err = -ENODEV; 3311da177e4SLinus Torvalds goto __error; 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds } 334ca2c0966STakashi Iwai data = kzalloc(sizeof(*data), GFP_KERNEL); 3351da177e4SLinus Torvalds if (data == NULL) { 3361da177e4SLinus Torvalds err = -ENOMEM; 3371da177e4SLinus Torvalds goto __error; 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds data->entry = entry; 3401da177e4SLinus Torvalds switch (entry->content) { 3411da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_TEXT: 3421da177e4SLinus Torvalds if (mode == O_RDONLY || mode == O_RDWR) { 343ca2c0966STakashi Iwai buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); 3447e4eeec8STakashi Iwai if (buffer == NULL) 3457e4eeec8STakashi Iwai goto __nomem; 3461da177e4SLinus Torvalds data->rbuffer = buffer; 3477e4eeec8STakashi Iwai buffer->len = PAGE_SIZE; 3487e4eeec8STakashi Iwai buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); 3497e4eeec8STakashi Iwai if (buffer->buffer == NULL) 3507e4eeec8STakashi Iwai goto __nomem; 3511da177e4SLinus Torvalds } 3521da177e4SLinus Torvalds if (mode == O_WRONLY || mode == O_RDWR) { 353ca2c0966STakashi Iwai buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); 3547e4eeec8STakashi Iwai if (buffer == NULL) 3557e4eeec8STakashi Iwai goto __nomem; 3561da177e4SLinus Torvalds data->wbuffer = buffer; 3577e4eeec8STakashi Iwai buffer->len = PAGE_SIZE; 3587e4eeec8STakashi Iwai buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); 3597e4eeec8STakashi Iwai if (buffer->buffer == NULL) 3607e4eeec8STakashi Iwai goto __nomem; 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds break; 3631da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: /* data */ 3641da177e4SLinus Torvalds if (entry->c.ops->open) { 3651da177e4SLinus Torvalds if ((err = entry->c.ops->open(entry, mode, 3661da177e4SLinus Torvalds &data->file_private_data)) < 0) { 3671da177e4SLinus Torvalds kfree(data); 3681da177e4SLinus Torvalds goto __error; 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds break; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds file->private_data = data; 3741a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 3751da177e4SLinus Torvalds if (entry->content == SNDRV_INFO_CONTENT_TEXT && 3761da177e4SLinus Torvalds (mode == O_RDONLY || mode == O_RDWR)) { 3771da177e4SLinus Torvalds if (entry->c.text.read) { 3781a60d4c5SIngo Molnar mutex_lock(&entry->access); 3791da177e4SLinus Torvalds entry->c.text.read(entry, data->rbuffer); 3801a60d4c5SIngo Molnar mutex_unlock(&entry->access); 3811da177e4SLinus Torvalds } 3821da177e4SLinus Torvalds } 3831da177e4SLinus Torvalds return 0; 3841da177e4SLinus Torvalds 3857e4eeec8STakashi Iwai __nomem: 3867e4eeec8STakashi Iwai if (data->rbuffer) { 3877e4eeec8STakashi Iwai kfree(data->rbuffer->buffer); 3887e4eeec8STakashi Iwai kfree(data->rbuffer); 3897e4eeec8STakashi Iwai } 3907e4eeec8STakashi Iwai if (data->wbuffer) { 3917e4eeec8STakashi Iwai kfree(data->wbuffer->buffer); 3927e4eeec8STakashi Iwai kfree(data->wbuffer); 3937e4eeec8STakashi Iwai } 3947e4eeec8STakashi Iwai kfree(data); 3957e4eeec8STakashi Iwai err = -ENOMEM; 3961da177e4SLinus Torvalds __error: 3971da177e4SLinus Torvalds module_put(entry->module); 3981da177e4SLinus Torvalds __error1: 3991a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 4001da177e4SLinus Torvalds return err; 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds static int snd_info_entry_release(struct inode *inode, struct file *file) 4041da177e4SLinus Torvalds { 40524c1f931STakashi Iwai struct snd_info_entry *entry; 40624c1f931STakashi Iwai struct snd_info_private_data *data; 4071da177e4SLinus Torvalds int mode; 4081da177e4SLinus Torvalds 4091da177e4SLinus Torvalds mode = file->f_flags & O_ACCMODE; 4101da177e4SLinus Torvalds data = file->private_data; 4111da177e4SLinus Torvalds entry = data->entry; 4121da177e4SLinus Torvalds switch (entry->content) { 4131da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_TEXT: 4147e4eeec8STakashi Iwai if (data->rbuffer) { 4157e4eeec8STakashi Iwai kfree(data->rbuffer->buffer); 4161da177e4SLinus Torvalds kfree(data->rbuffer); 4171da177e4SLinus Torvalds } 4187e4eeec8STakashi Iwai if (data->wbuffer) { 4191da177e4SLinus Torvalds if (entry->c.text.write) { 4201da177e4SLinus Torvalds entry->c.text.write(entry, data->wbuffer); 4211da177e4SLinus Torvalds if (data->wbuffer->error) { 4221da177e4SLinus Torvalds snd_printk(KERN_WARNING "data write error to %s (%i)\n", 4231da177e4SLinus Torvalds entry->name, 4241da177e4SLinus Torvalds data->wbuffer->error); 4251da177e4SLinus Torvalds } 4261da177e4SLinus Torvalds } 4277e4eeec8STakashi Iwai kfree(data->wbuffer->buffer); 4281da177e4SLinus Torvalds kfree(data->wbuffer); 4291da177e4SLinus Torvalds } 4301da177e4SLinus Torvalds break; 4311da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 4321da177e4SLinus Torvalds if (entry->c.ops->release) 4331da177e4SLinus Torvalds entry->c.ops->release(entry, mode, 4341da177e4SLinus Torvalds data->file_private_data); 4351da177e4SLinus Torvalds break; 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds module_put(entry->module); 4381da177e4SLinus Torvalds kfree(data); 4391da177e4SLinus Torvalds return 0; 4401da177e4SLinus Torvalds } 4411da177e4SLinus Torvalds 4421da177e4SLinus Torvalds static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) 4431da177e4SLinus Torvalds { 44424c1f931STakashi Iwai struct snd_info_private_data *data; 4451da177e4SLinus Torvalds struct snd_info_entry *entry; 4461da177e4SLinus Torvalds unsigned int mask; 4471da177e4SLinus Torvalds 4481da177e4SLinus Torvalds data = file->private_data; 4491da177e4SLinus Torvalds if (data == NULL) 4501da177e4SLinus Torvalds return 0; 4511da177e4SLinus Torvalds entry = data->entry; 4521da177e4SLinus Torvalds mask = 0; 4531da177e4SLinus Torvalds switch (entry->content) { 4541da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 4551da177e4SLinus Torvalds if (entry->c.ops->poll) 4561da177e4SLinus Torvalds return entry->c.ops->poll(entry, 4571da177e4SLinus Torvalds data->file_private_data, 4581da177e4SLinus Torvalds file, wait); 4591da177e4SLinus Torvalds if (entry->c.ops->read) 4601da177e4SLinus Torvalds mask |= POLLIN | POLLRDNORM; 4611da177e4SLinus Torvalds if (entry->c.ops->write) 4621da177e4SLinus Torvalds mask |= POLLOUT | POLLWRNORM; 4631da177e4SLinus Torvalds break; 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds return mask; 4661da177e4SLinus Torvalds } 4671da177e4SLinus Torvalds 468d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, 469d99e9889SIngo Molnar unsigned long arg) 4701da177e4SLinus Torvalds { 47124c1f931STakashi Iwai struct snd_info_private_data *data; 4721da177e4SLinus Torvalds struct snd_info_entry *entry; 4731da177e4SLinus Torvalds 4741da177e4SLinus Torvalds data = file->private_data; 4751da177e4SLinus Torvalds if (data == NULL) 4761da177e4SLinus Torvalds return 0; 4771da177e4SLinus Torvalds entry = data->entry; 4781da177e4SLinus Torvalds switch (entry->content) { 4791da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 4801da177e4SLinus Torvalds if (entry->c.ops->ioctl) 4811da177e4SLinus Torvalds return entry->c.ops->ioctl(entry, 4821da177e4SLinus Torvalds data->file_private_data, 4831da177e4SLinus Torvalds file, cmd, arg); 4841da177e4SLinus Torvalds break; 4851da177e4SLinus Torvalds } 4861da177e4SLinus Torvalds return -ENOTTY; 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds 4891da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) 4901da177e4SLinus Torvalds { 4917bc56323SJosef Sipek struct inode *inode = file->f_path.dentry->d_inode; 49224c1f931STakashi Iwai struct snd_info_private_data *data; 4931da177e4SLinus Torvalds struct snd_info_entry *entry; 4941da177e4SLinus Torvalds 4951da177e4SLinus Torvalds data = file->private_data; 4961da177e4SLinus Torvalds if (data == NULL) 4971da177e4SLinus Torvalds return 0; 4981da177e4SLinus Torvalds entry = data->entry; 4991da177e4SLinus Torvalds switch (entry->content) { 5001da177e4SLinus Torvalds case SNDRV_INFO_CONTENT_DATA: 5011da177e4SLinus Torvalds if (entry->c.ops->mmap) 5021da177e4SLinus Torvalds return entry->c.ops->mmap(entry, 5031da177e4SLinus Torvalds data->file_private_data, 5041da177e4SLinus Torvalds inode, file, vma); 5051da177e4SLinus Torvalds break; 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds return -ENXIO; 5081da177e4SLinus Torvalds } 5091da177e4SLinus Torvalds 5109c2e08c5SArjan van de Ven static const struct file_operations snd_info_entry_operations = 5111da177e4SLinus Torvalds { 5121da177e4SLinus Torvalds .owner = THIS_MODULE, 5131da177e4SLinus Torvalds .llseek = snd_info_entry_llseek, 5141da177e4SLinus Torvalds .read = snd_info_entry_read, 5151da177e4SLinus Torvalds .write = snd_info_entry_write, 5161da177e4SLinus Torvalds .poll = snd_info_entry_poll, 517d99e9889SIngo Molnar .unlocked_ioctl = snd_info_entry_ioctl, 5181da177e4SLinus Torvalds .mmap = snd_info_entry_mmap, 5191da177e4SLinus Torvalds .open = snd_info_entry_open, 5201da177e4SLinus Torvalds .release = snd_info_entry_release, 5211da177e4SLinus Torvalds }; 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds /** 5241da177e4SLinus Torvalds * snd_create_proc_entry - create a procfs entry 5251da177e4SLinus Torvalds * @name: the name of the proc file 5261da177e4SLinus Torvalds * @mode: the file permission bits, S_Ixxx 5271da177e4SLinus Torvalds * @parent: the parent proc-directory entry 5281da177e4SLinus Torvalds * 5291da177e4SLinus Torvalds * Creates a new proc file entry with the given name and permission 5301da177e4SLinus Torvalds * on the given directory. 5311da177e4SLinus Torvalds * 5321da177e4SLinus Torvalds * Returns the pointer of new instance or NULL on failure. 5331da177e4SLinus Torvalds */ 5341da177e4SLinus Torvalds static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, 5351da177e4SLinus Torvalds struct proc_dir_entry *parent) 5361da177e4SLinus Torvalds { 5371da177e4SLinus Torvalds struct proc_dir_entry *p; 5381da177e4SLinus Torvalds p = create_proc_entry(name, mode, parent); 5391da177e4SLinus Torvalds if (p) 5401da177e4SLinus Torvalds snd_info_entry_prepare(p); 5411da177e4SLinus Torvalds return p; 5421da177e4SLinus Torvalds } 5431da177e4SLinus Torvalds 5441da177e4SLinus Torvalds int __init snd_info_init(void) 5451da177e4SLinus Torvalds { 5461da177e4SLinus Torvalds struct proc_dir_entry *p; 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root); 5491da177e4SLinus Torvalds if (p == NULL) 5501da177e4SLinus Torvalds return -ENOMEM; 5511da177e4SLinus Torvalds snd_proc_root = p; 5521da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 5531da177e4SLinus Torvalds { 55424c1f931STakashi Iwai struct snd_info_entry *entry; 5551da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", 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_oss_root = entry; 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds #endif 5651da177e4SLinus Torvalds #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) 5661da177e4SLinus Torvalds { 56724c1f931STakashi Iwai struct snd_info_entry *entry; 5681da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL) 5691da177e4SLinus Torvalds return -ENOMEM; 5701da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 5711da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5721da177e4SLinus Torvalds snd_info_free_entry(entry); 5731da177e4SLinus Torvalds return -ENOMEM; 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds snd_seq_root = entry; 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds #endif 5781da177e4SLinus Torvalds snd_info_version_init(); 5791da177e4SLinus Torvalds snd_minor_info_init(); 5801da177e4SLinus Torvalds snd_minor_info_oss_init(); 5811da177e4SLinus Torvalds snd_card_info_init(); 5821da177e4SLinus Torvalds return 0; 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds int __exit snd_info_done(void) 5861da177e4SLinus Torvalds { 5871da177e4SLinus Torvalds snd_card_info_done(); 5881da177e4SLinus Torvalds snd_minor_info_oss_done(); 5891da177e4SLinus Torvalds snd_minor_info_done(); 5901da177e4SLinus Torvalds snd_info_version_done(); 5911da177e4SLinus Torvalds if (snd_proc_root) { 5921da177e4SLinus Torvalds #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) 593746d4a02STakashi Iwai snd_info_free_entry(snd_seq_root); 5941da177e4SLinus Torvalds #endif 5951da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL 596746d4a02STakashi Iwai snd_info_free_entry(snd_oss_root); 5971da177e4SLinus Torvalds #endif 5981da177e4SLinus Torvalds snd_remove_proc_entry(&proc_root, snd_proc_root); 5991da177e4SLinus Torvalds } 6001da177e4SLinus Torvalds return 0; 6011da177e4SLinus Torvalds } 6021da177e4SLinus Torvalds 6031da177e4SLinus Torvalds /* 6041da177e4SLinus Torvalds 6051da177e4SLinus Torvalds */ 6061da177e4SLinus Torvalds 6071da177e4SLinus Torvalds 6081da177e4SLinus Torvalds /* 6091da177e4SLinus Torvalds * create a card proc file 6101da177e4SLinus Torvalds * called from init.c 6111da177e4SLinus Torvalds */ 61224c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card) 6131da177e4SLinus Torvalds { 6141da177e4SLinus Torvalds char str[8]; 61524c1f931STakashi Iwai struct snd_info_entry *entry; 6161da177e4SLinus Torvalds 6171da177e4SLinus Torvalds snd_assert(card != NULL, return -ENXIO); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds sprintf(str, "card%i", card->number); 6201da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) 6211da177e4SLinus Torvalds return -ENOMEM; 6221da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 6231da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 6241da177e4SLinus Torvalds snd_info_free_entry(entry); 6251da177e4SLinus Torvalds return -ENOMEM; 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds card->proc_root = entry; 6281da177e4SLinus Torvalds return 0; 6291da177e4SLinus Torvalds } 6301da177e4SLinus Torvalds 6311da177e4SLinus Torvalds /* 6321da177e4SLinus Torvalds * register the card proc file 6331da177e4SLinus Torvalds * called from init.c 6341da177e4SLinus Torvalds */ 63524c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card) 6361da177e4SLinus Torvalds { 6371da177e4SLinus Torvalds struct proc_dir_entry *p; 6381da177e4SLinus Torvalds 6391da177e4SLinus Torvalds snd_assert(card != NULL, return -ENXIO); 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds if (!strcmp(card->id, card->proc_root->name)) 6421da177e4SLinus Torvalds return 0; 6431da177e4SLinus Torvalds 6441da177e4SLinus Torvalds p = proc_symlink(card->id, snd_proc_root, card->proc_root->name); 6451da177e4SLinus Torvalds if (p == NULL) 6461da177e4SLinus Torvalds return -ENOMEM; 6471da177e4SLinus Torvalds card->proc_root_link = p; 6481da177e4SLinus Torvalds return 0; 6491da177e4SLinus Torvalds } 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds /* 6521da177e4SLinus Torvalds * de-register the card proc file 6531da177e4SLinus Torvalds * called from init.c 6541da177e4SLinus Torvalds */ 655746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card) 6561da177e4SLinus Torvalds { 657746d4a02STakashi Iwai snd_assert(card != NULL, return); 658746d4a02STakashi Iwai mutex_lock(&info_mutex); 6591da177e4SLinus Torvalds if (card->proc_root_link) { 6601da177e4SLinus Torvalds snd_remove_proc_entry(snd_proc_root, card->proc_root_link); 6611da177e4SLinus Torvalds card->proc_root_link = NULL; 6621da177e4SLinus Torvalds } 663746d4a02STakashi Iwai if (card->proc_root) 664746d4a02STakashi Iwai snd_info_disconnect(card->proc_root); 665746d4a02STakashi Iwai mutex_unlock(&info_mutex); 6661da177e4SLinus Torvalds } 667746d4a02STakashi Iwai 668746d4a02STakashi Iwai /* 669746d4a02STakashi Iwai * release the card proc file resources 670746d4a02STakashi Iwai * called from init.c 671746d4a02STakashi Iwai */ 672746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card) 673746d4a02STakashi Iwai { 674746d4a02STakashi Iwai snd_assert(card != NULL, return -ENXIO); 675746d4a02STakashi Iwai snd_info_free_entry(card->proc_root); 676746d4a02STakashi Iwai card->proc_root = NULL; 6771da177e4SLinus Torvalds return 0; 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds /** 6821da177e4SLinus Torvalds * snd_info_get_line - read one line from the procfs buffer 6831da177e4SLinus Torvalds * @buffer: the procfs buffer 6841da177e4SLinus Torvalds * @line: the buffer to store 6851da177e4SLinus Torvalds * @len: the max. buffer size - 1 6861da177e4SLinus Torvalds * 6871da177e4SLinus Torvalds * Reads one line from the buffer and stores the string. 6881da177e4SLinus Torvalds * 6891da177e4SLinus Torvalds * Returns zero if successful, or 1 if error or EOF. 6901da177e4SLinus Torvalds */ 69124c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) 6921da177e4SLinus Torvalds { 6931da177e4SLinus Torvalds int c = -1; 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds if (len <= 0 || buffer->stop || buffer->error) 6961da177e4SLinus Torvalds return 1; 6971da177e4SLinus Torvalds while (--len > 0) { 6987e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++]; 6991da177e4SLinus Torvalds if (c == '\n') { 7007e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) 7011da177e4SLinus Torvalds buffer->stop = 1; 7021da177e4SLinus Torvalds break; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds *line++ = c; 7057e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) { 7061da177e4SLinus Torvalds buffer->stop = 1; 7071da177e4SLinus Torvalds break; 7081da177e4SLinus Torvalds } 7091da177e4SLinus Torvalds } 7101da177e4SLinus Torvalds while (c != '\n' && !buffer->stop) { 7117e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++]; 7127e4eeec8STakashi Iwai if (buffer->curr >= buffer->size) 7131da177e4SLinus Torvalds buffer->stop = 1; 7141da177e4SLinus Torvalds } 7151da177e4SLinus Torvalds *line = '\0'; 7161da177e4SLinus Torvalds return 0; 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds 719c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line); 720c0d3fb39STakashi Iwai 7211da177e4SLinus Torvalds /** 722856def8aSHenrik Kretzschmar * snd_info_get_str - parse a string token 7231da177e4SLinus Torvalds * @dest: the buffer to store the string token 7241da177e4SLinus Torvalds * @src: the original string 7251da177e4SLinus Torvalds * @len: the max. length of token - 1 7261da177e4SLinus Torvalds * 7271da177e4SLinus Torvalds * Parses the original string and copy a token to the given 7281da177e4SLinus Torvalds * string buffer. 7291da177e4SLinus Torvalds * 7301da177e4SLinus Torvalds * Returns the updated pointer of the original string so that 7311da177e4SLinus Torvalds * it can be used for the next call. 7321da177e4SLinus Torvalds */ 7331da177e4SLinus Torvalds char *snd_info_get_str(char *dest, char *src, int len) 7341da177e4SLinus Torvalds { 7351da177e4SLinus Torvalds int c; 7361da177e4SLinus Torvalds 7371da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 7381da177e4SLinus Torvalds src++; 7391da177e4SLinus Torvalds if (*src == '"' || *src == '\'') { 7401da177e4SLinus Torvalds c = *src++; 7411da177e4SLinus Torvalds while (--len > 0 && *src && *src != c) { 7421da177e4SLinus Torvalds *dest++ = *src++; 7431da177e4SLinus Torvalds } 7441da177e4SLinus Torvalds if (*src == c) 7451da177e4SLinus Torvalds src++; 7461da177e4SLinus Torvalds } else { 7471da177e4SLinus Torvalds while (--len > 0 && *src && *src != ' ' && *src != '\t') { 7481da177e4SLinus Torvalds *dest++ = *src++; 7491da177e4SLinus Torvalds } 7501da177e4SLinus Torvalds } 7511da177e4SLinus Torvalds *dest = 0; 7521da177e4SLinus Torvalds while (*src == ' ' || *src == '\t') 7531da177e4SLinus Torvalds src++; 7541da177e4SLinus Torvalds return src; 7551da177e4SLinus Torvalds } 7561da177e4SLinus Torvalds 757c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str); 758c0d3fb39STakashi Iwai 7591da177e4SLinus Torvalds /** 7601da177e4SLinus Torvalds * snd_info_create_entry - create an info entry 7611da177e4SLinus Torvalds * @name: the proc file name 7621da177e4SLinus Torvalds * 7631da177e4SLinus Torvalds * Creates an info entry with the given file name and initializes as 7641da177e4SLinus Torvalds * the default state. 7651da177e4SLinus Torvalds * 7661da177e4SLinus Torvalds * Usually called from other functions such as 7671da177e4SLinus Torvalds * snd_info_create_card_entry(). 7681da177e4SLinus Torvalds * 7691da177e4SLinus Torvalds * Returns the pointer of the new instance, or NULL on failure. 7701da177e4SLinus Torvalds */ 77124c1f931STakashi Iwai static struct snd_info_entry *snd_info_create_entry(const char *name) 7721da177e4SLinus Torvalds { 77324c1f931STakashi Iwai struct snd_info_entry *entry; 774ca2c0966STakashi Iwai entry = kzalloc(sizeof(*entry), GFP_KERNEL); 7751da177e4SLinus Torvalds if (entry == NULL) 7761da177e4SLinus Torvalds return NULL; 777543537bdSPaulo Marques entry->name = kstrdup(name, GFP_KERNEL); 7781da177e4SLinus Torvalds if (entry->name == NULL) { 7791da177e4SLinus Torvalds kfree(entry); 7801da177e4SLinus Torvalds return NULL; 7811da177e4SLinus Torvalds } 7821da177e4SLinus Torvalds entry->mode = S_IFREG | S_IRUGO; 7831da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_TEXT; 7841a60d4c5SIngo Molnar mutex_init(&entry->access); 785746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->children); 786746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->list); 7871da177e4SLinus Torvalds return entry; 7881da177e4SLinus Torvalds } 7891da177e4SLinus Torvalds 7901da177e4SLinus Torvalds /** 7911da177e4SLinus Torvalds * snd_info_create_module_entry - create an info entry for the given module 7921da177e4SLinus Torvalds * @module: the module pointer 7931da177e4SLinus Torvalds * @name: the file name 7941da177e4SLinus Torvalds * @parent: the parent directory 7951da177e4SLinus Torvalds * 7961da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given module. 7971da177e4SLinus Torvalds * 7981da177e4SLinus Torvalds * Returns the pointer of the new instance, or NULL on failure. 7991da177e4SLinus Torvalds */ 80024c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module, 8011da177e4SLinus Torvalds const char *name, 80224c1f931STakashi Iwai struct snd_info_entry *parent) 8031da177e4SLinus Torvalds { 80424c1f931STakashi Iwai struct snd_info_entry *entry = snd_info_create_entry(name); 8051da177e4SLinus Torvalds if (entry) { 8061da177e4SLinus Torvalds entry->module = module; 8071da177e4SLinus Torvalds entry->parent = parent; 8081da177e4SLinus Torvalds } 8091da177e4SLinus Torvalds return entry; 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds 812c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry); 813c0d3fb39STakashi Iwai 8141da177e4SLinus Torvalds /** 8151da177e4SLinus Torvalds * snd_info_create_card_entry - create an info entry for the given card 8161da177e4SLinus Torvalds * @card: the card instance 8171da177e4SLinus Torvalds * @name: the file name 8181da177e4SLinus Torvalds * @parent: the parent directory 8191da177e4SLinus Torvalds * 8201da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card. 8211da177e4SLinus Torvalds * 8221da177e4SLinus Torvalds * Returns the pointer of the new instance, or NULL on failure. 8231da177e4SLinus Torvalds */ 82424c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, 8251da177e4SLinus Torvalds const char *name, 82624c1f931STakashi Iwai struct snd_info_entry * parent) 8271da177e4SLinus Torvalds { 82824c1f931STakashi Iwai struct snd_info_entry *entry = snd_info_create_entry(name); 8291da177e4SLinus Torvalds if (entry) { 8301da177e4SLinus Torvalds entry->module = card->module; 8311da177e4SLinus Torvalds entry->card = card; 8321da177e4SLinus Torvalds entry->parent = parent; 8331da177e4SLinus Torvalds } 8341da177e4SLinus Torvalds return entry; 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds 837c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry); 838c0d3fb39STakashi Iwai 839746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry) 840746d4a02STakashi Iwai { 841746d4a02STakashi Iwai struct list_head *p, *n; 842746d4a02STakashi Iwai struct proc_dir_entry *root; 843746d4a02STakashi Iwai 844746d4a02STakashi Iwai list_for_each_safe(p, n, &entry->children) { 845746d4a02STakashi Iwai snd_info_disconnect(list_entry(p, struct snd_info_entry, list)); 846746d4a02STakashi Iwai } 847746d4a02STakashi Iwai 848746d4a02STakashi Iwai if (! entry->p) 849746d4a02STakashi Iwai return; 850746d4a02STakashi Iwai list_del_init(&entry->list); 851746d4a02STakashi Iwai root = entry->parent == NULL ? snd_proc_root : entry->parent->p; 852746d4a02STakashi Iwai snd_assert(root, return); 853746d4a02STakashi Iwai snd_remove_proc_entry(root, entry->p); 854746d4a02STakashi Iwai entry->p = NULL; 855746d4a02STakashi Iwai } 856746d4a02STakashi Iwai 85724c1f931STakashi Iwai static int snd_info_dev_free_entry(struct snd_device *device) 8581da177e4SLinus Torvalds { 85924c1f931STakashi Iwai struct snd_info_entry *entry = device->device_data; 8601da177e4SLinus Torvalds snd_info_free_entry(entry); 8611da177e4SLinus Torvalds return 0; 8621da177e4SLinus Torvalds } 8631da177e4SLinus Torvalds 86424c1f931STakashi Iwai static int snd_info_dev_register_entry(struct snd_device *device) 8651da177e4SLinus Torvalds { 86624c1f931STakashi Iwai struct snd_info_entry *entry = device->device_data; 8671da177e4SLinus Torvalds return snd_info_register(entry); 8681da177e4SLinus Torvalds } 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds /** 8711da177e4SLinus Torvalds * snd_card_proc_new - create an info entry for the given card 8721da177e4SLinus Torvalds * @card: the card instance 8731da177e4SLinus Torvalds * @name: the file name 8741da177e4SLinus Torvalds * @entryp: the pointer to store the new info entry 8751da177e4SLinus Torvalds * 8761da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card. 8771da177e4SLinus Torvalds * Unlike snd_info_create_card_entry(), this function registers the 8781da177e4SLinus Torvalds * info entry as an ALSA device component, so that it can be 8791da177e4SLinus Torvalds * unregistered/released without explicit call. 8801da177e4SLinus Torvalds * Also, you don't have to register this entry via snd_info_register(), 8811da177e4SLinus Torvalds * since this will be registered by snd_card_register() automatically. 8821da177e4SLinus Torvalds * 8831da177e4SLinus Torvalds * The parent is assumed as card->proc_root. 8841da177e4SLinus Torvalds * 8851da177e4SLinus Torvalds * For releasing this entry, use snd_device_free() instead of 8861da177e4SLinus Torvalds * snd_info_free_entry(). 8871da177e4SLinus Torvalds * 8881da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 8891da177e4SLinus Torvalds */ 89024c1f931STakashi Iwai int snd_card_proc_new(struct snd_card *card, const char *name, 89124c1f931STakashi Iwai struct snd_info_entry **entryp) 8921da177e4SLinus Torvalds { 89324c1f931STakashi Iwai static struct snd_device_ops ops = { 8941da177e4SLinus Torvalds .dev_free = snd_info_dev_free_entry, 8951da177e4SLinus Torvalds .dev_register = snd_info_dev_register_entry, 896746d4a02STakashi Iwai /* disconnect is done via snd_info_card_disconnect() */ 8971da177e4SLinus Torvalds }; 89824c1f931STakashi Iwai struct snd_info_entry *entry; 8991da177e4SLinus Torvalds int err; 9001da177e4SLinus Torvalds 9011da177e4SLinus Torvalds entry = snd_info_create_card_entry(card, name, card->proc_root); 9021da177e4SLinus Torvalds if (! entry) 9031da177e4SLinus Torvalds return -ENOMEM; 9041da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) { 9051da177e4SLinus Torvalds snd_info_free_entry(entry); 9061da177e4SLinus Torvalds return err; 9071da177e4SLinus Torvalds } 9081da177e4SLinus Torvalds if (entryp) 9091da177e4SLinus Torvalds *entryp = entry; 9101da177e4SLinus Torvalds return 0; 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds 913c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_card_proc_new); 914c0d3fb39STakashi Iwai 9151da177e4SLinus Torvalds /** 9161da177e4SLinus Torvalds * snd_info_free_entry - release the info entry 9171da177e4SLinus Torvalds * @entry: the info entry 9181da177e4SLinus Torvalds * 9191da177e4SLinus Torvalds * Releases the info entry. Don't call this after registered. 9201da177e4SLinus Torvalds */ 92124c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry) 9221da177e4SLinus Torvalds { 9231da177e4SLinus Torvalds if (entry == NULL) 9241da177e4SLinus Torvalds return; 925746d4a02STakashi Iwai if (entry->p) { 926746d4a02STakashi Iwai mutex_lock(&info_mutex); 927746d4a02STakashi Iwai snd_info_disconnect(entry); 928746d4a02STakashi Iwai mutex_unlock(&info_mutex); 929746d4a02STakashi Iwai } 9301da177e4SLinus Torvalds kfree(entry->name); 9311da177e4SLinus Torvalds if (entry->private_free) 9321da177e4SLinus Torvalds entry->private_free(entry); 9331da177e4SLinus Torvalds kfree(entry); 9341da177e4SLinus Torvalds } 9351da177e4SLinus Torvalds 936c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry); 937c0d3fb39STakashi Iwai 9381da177e4SLinus Torvalds /** 9391da177e4SLinus Torvalds * snd_info_register - register the info entry 9401da177e4SLinus Torvalds * @entry: the info entry 9411da177e4SLinus Torvalds * 9421da177e4SLinus Torvalds * Registers the proc info entry. 9431da177e4SLinus Torvalds * 9441da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 9451da177e4SLinus Torvalds */ 94624c1f931STakashi Iwai int snd_info_register(struct snd_info_entry * entry) 9471da177e4SLinus Torvalds { 9481da177e4SLinus Torvalds struct proc_dir_entry *root, *p = NULL; 9491da177e4SLinus Torvalds 9501da177e4SLinus Torvalds snd_assert(entry != NULL, return -ENXIO); 9511da177e4SLinus Torvalds root = entry->parent == NULL ? snd_proc_root : entry->parent->p; 9521a60d4c5SIngo Molnar mutex_lock(&info_mutex); 9531da177e4SLinus Torvalds p = snd_create_proc_entry(entry->name, entry->mode, root); 9541da177e4SLinus Torvalds if (!p) { 9551a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 9561da177e4SLinus Torvalds return -ENOMEM; 9571da177e4SLinus Torvalds } 9581da177e4SLinus Torvalds p->owner = entry->module; 9591da177e4SLinus Torvalds if (!S_ISDIR(entry->mode)) 9601da177e4SLinus Torvalds p->proc_fops = &snd_info_entry_operations; 9611da177e4SLinus Torvalds p->size = entry->size; 9621da177e4SLinus Torvalds p->data = entry; 9631da177e4SLinus Torvalds entry->p = p; 964746d4a02STakashi Iwai if (entry->parent) 965746d4a02STakashi Iwai list_add_tail(&entry->list, &entry->parent->children); 9661a60d4c5SIngo Molnar mutex_unlock(&info_mutex); 9671da177e4SLinus Torvalds return 0; 9681da177e4SLinus Torvalds } 9691da177e4SLinus Torvalds 970c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register); 971c0d3fb39STakashi Iwai 9721da177e4SLinus Torvalds /* 9731da177e4SLinus Torvalds 9741da177e4SLinus Torvalds */ 9751da177e4SLinus Torvalds 9766581f4e7STakashi Iwai static struct snd_info_entry *snd_info_version_entry; 9771da177e4SLinus Torvalds 97824c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 9791da177e4SLinus Torvalds { 9801da177e4SLinus Torvalds snd_iprintf(buffer, 9811da177e4SLinus Torvalds "Advanced Linux Sound Architecture Driver Version " 9821da177e4SLinus Torvalds CONFIG_SND_VERSION CONFIG_SND_DATE ".\n" 9831da177e4SLinus Torvalds ); 9841da177e4SLinus Torvalds } 9851da177e4SLinus Torvalds 9861da177e4SLinus Torvalds static int __init snd_info_version_init(void) 9871da177e4SLinus Torvalds { 98824c1f931STakashi Iwai struct snd_info_entry *entry; 9891da177e4SLinus Torvalds 9901da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); 9911da177e4SLinus Torvalds if (entry == NULL) 9921da177e4SLinus Torvalds return -ENOMEM; 9931da177e4SLinus Torvalds entry->c.text.read = snd_info_version_read; 9941da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 9951da177e4SLinus Torvalds snd_info_free_entry(entry); 9961da177e4SLinus Torvalds return -ENOMEM; 9971da177e4SLinus Torvalds } 9981da177e4SLinus Torvalds snd_info_version_entry = entry; 9991da177e4SLinus Torvalds return 0; 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds 10021da177e4SLinus Torvalds static int __exit snd_info_version_done(void) 10031da177e4SLinus Torvalds { 1004746d4a02STakashi Iwai snd_info_free_entry(snd_info_version_entry); 10051da177e4SLinus Torvalds return 0; 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds 10081da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 1009