ALSA: compress: fix the return value for SNDRV_COMPRESS_VERSION
[linux-2.6.git] / sound / core / info.c
index 4188f76..c1e611c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Information interface for ALSA driver
- *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  *
  *
  *   This program is free software; you can redistribute it and/or modify
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/time.h>
-#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/module.h>
 #include <sound/core.h>
 #include <sound/minors.h>
 #include <sound/info.h>
 #include <sound/version.h>
 #include <linux/proc_fs.h>
-#include <linux/devfs_fs_kernel.h>
 #include <linux/mutex.h>
 #include <stdarg.h>
 
@@ -79,6 +79,7 @@ struct snd_info_private_data {
 
 static int snd_info_version_init(void);
 static int snd_info_version_done(void);
+static void snd_info_disconnect(struct snd_info_entry *entry);
 
 
 /* resize the proc r/w buffer */
@@ -88,12 +89,10 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
        char *nbuf;
 
        nsize = PAGE_ALIGN(nsize);
-       nbuf = kmalloc(nsize, GFP_KERNEL);
+       nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
        if (! nbuf)
                return -ENOMEM;
 
-       memcpy(nbuf, buffer->buffer, buffer->len);
-       kfree(buffer->buffer);
        buffer->buffer = nbuf;
        buffer->len = nsize;
        return 0;
@@ -108,7 +107,7 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
  *
  * Returns the size of output string.
  */
-int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...)
+int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
 {
        va_list args;
        int len, res;
@@ -120,7 +119,10 @@ int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...)
        len = buffer->len - buffer->size;
        va_start(args, fmt);
        for (;;) {
-               res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, args);
+               va_list ap;
+               va_copy(ap, args);
+               res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap);
+               va_end(ap);
                if (res < len)
                        break;
                err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
@@ -143,19 +145,14 @@ EXPORT_SYMBOL(snd_iprintf);
 
  */
 
-static struct proc_dir_entry *snd_proc_root = NULL;
-struct snd_info_entry *snd_seq_root = NULL;
+static struct proc_dir_entry *snd_proc_root;
+struct snd_info_entry *snd_seq_root;
 EXPORT_SYMBOL(snd_seq_root);
 
 #ifdef CONFIG_SND_OSSEMUL
-struct snd_info_entry *snd_oss_root = NULL;
+struct snd_info_entry *snd_oss_root;
 #endif
 
-static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
-{
-       de->owner = THIS_MODULE;
-}
-
 static void snd_remove_proc_entry(struct proc_dir_entry *parent,
                                  struct proc_dir_entry *de)
 {
@@ -167,40 +164,44 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
 {
        struct snd_info_private_data *data;
        struct snd_info_entry *entry;
-       loff_t ret;
+       loff_t ret = -EINVAL, size;
 
        data = file->private_data;
        entry = data->entry;
-       lock_kernel();
-       switch (entry->content) {
-       case SNDRV_INFO_CONTENT_TEXT:
-               switch (orig) {
-               case 0: /* SEEK_SET */
-                       file->f_pos = offset;
-                       ret = file->f_pos;
-                       goto out;
-               case 1: /* SEEK_CUR */
-                       file->f_pos += offset;
-                       ret = file->f_pos;
-                       goto out;
-               case 2: /* SEEK_END */
-               default:
-                       ret = -EINVAL;
-                       goto out;
-               }
+       mutex_lock(&entry->access);
+       if (entry->content == SNDRV_INFO_CONTENT_DATA &&
+           entry->c.ops->llseek) {
+               offset = entry->c.ops->llseek(entry,
+                                             data->file_private_data,
+                                             file, offset, orig);
+               goto out;
+       }
+       if (entry->content == SNDRV_INFO_CONTENT_DATA)
+               size = entry->size;
+       else
+               size = 0;
+       switch (orig) {
+       case SEEK_SET:
                break;
-       case SNDRV_INFO_CONTENT_DATA:
-               if (entry->c.ops->llseek) {
-                       ret = entry->c.ops->llseek(entry,
-                                                   data->file_private_data,
-                                                   file, offset, orig);
+       case SEEK_CUR:
+               offset += file->f_pos;
+               break;
+       case SEEK_END:
+               if (!size)
                        goto out;
-               }
+               offset += size;
                break;
-       }
-       ret = -ENXIO;
-out:
-       unlock_kernel();
+       default:
+               goto out;
+       }
+       if (offset < 0)
+               goto out;
+       if (size && offset > size)
+               offset = size;
+       file->f_pos = offset;
+       ret = offset;
+ out:
+       mutex_unlock(&entry->access);
        return ret;
 }
 
@@ -214,7 +215,8 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
        loff_t pos;
 
        data = file->private_data;
-       snd_assert(data != NULL, return -ENXIO);
+       if (snd_BUG_ON(!data))
+               return -ENXIO;
        pos = *offset;
        if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
                return -EIO;
@@ -234,10 +236,15 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
                        return -EFAULT;
                break;
        case SNDRV_INFO_CONTENT_DATA:
-               if (entry->c.ops->read)
+               if (pos >= entry->size)
+                       return 0;
+               if (entry->c.ops->read) {
+                       size = entry->size - pos;
+                       size = min(count, size);
                        size = entry->c.ops->read(entry,
                                                  data->file_private_data,
-                                                 file, buffer, count, pos);
+                                                 file, buffer, size, pos);
+               }
                break;
        }
        if ((ssize_t) size > 0)
@@ -255,7 +262,8 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer
        loff_t pos;
 
        data = file->private_data;
-       snd_assert(data != NULL, return -ENXIO);
+       if (snd_BUG_ON(!data))
+               return -ENXIO;
        entry = data->entry;
        pos = *offset;
        if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
@@ -267,7 +275,7 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer
                buf = data->wbuffer;
                if (buf == NULL)
                        return -EIO;
-               mutex_unlock(&entry->access);
+               mutex_lock(&entry->access);
                if (pos + count >= buf->len) {
                        if (resize_info_buffer(buf, pos + count)) {
                                mutex_unlock(&entry->access);
@@ -283,10 +291,13 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer
                size = count;
                break;
        case SNDRV_INFO_CONTENT_DATA:
-               if (entry->c.ops->write)
+               if (entry->c.ops->write && count > 0) {
+                       size_t maxsize = entry->size - pos;
+                       count = min(count, maxsize);
                        size = entry->c.ops->write(entry,
                                                   data->file_private_data,
                                                   file, buffer, count, pos);
+               }
                break;
        }
        if ((ssize_t) size > 0)
@@ -305,7 +316,7 @@ static int snd_info_entry_open(struct inode *inode, struct file *file)
        mutex_lock(&info_mutex);
        p = PDE(inode);
        entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
-       if (entry == NULL || entry->disconnected) {
+       if (entry == NULL || ! entry->p) {
                mutex_unlock(&info_mutex);
                return -ENODEV;
        }
@@ -485,7 +496,7 @@ static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
 
 static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
 {
-       struct inode *inode = file->f_dentry->d_inode;
+       struct inode *inode = file->f_path.dentry->d_inode;
        struct snd_info_private_data *data;
        struct snd_info_entry *entry;
 
@@ -504,7 +515,7 @@ static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
        return -ENXIO;
 }
 
-static struct file_operations snd_info_entry_operations =
+static const struct file_operations snd_info_entry_operations =
 {
        .owner =                THIS_MODULE,
        .llseek =               snd_info_entry_llseek,
@@ -517,32 +528,11 @@ static struct file_operations snd_info_entry_operations =
        .release =              snd_info_entry_release,
 };
 
-/**
- * snd_create_proc_entry - create a procfs entry
- * @name: the name of the proc file
- * @mode: the file permission bits, S_Ixxx
- * @parent: the parent proc-directory entry
- *
- * Creates a new proc file entry with the given name and permission
- * on the given directory.
- *
- * Returns the pointer of new instance or NULL on failure.
- */
-static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
-                                                   struct proc_dir_entry *parent)
-{
-       struct proc_dir_entry *p;
-       p = create_proc_entry(name, mode, parent);
-       if (p)
-               snd_info_entry_prepare(p);
-       return p;
-}
-
 int __init snd_info_init(void)
 {
        struct proc_dir_entry *p;
 
-       p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root);
+       p = proc_mkdir("asound", NULL);
        if (p == NULL)
                return -ENOMEM;
        snd_proc_root = p;
@@ -587,12 +577,12 @@ int __exit snd_info_done(void)
        snd_info_version_done();
        if (snd_proc_root) {
 #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
-               snd_info_unregister(snd_seq_root);
+               snd_info_free_entry(snd_seq_root);
 #endif
 #ifdef CONFIG_SND_OSSEMUL
-               snd_info_unregister(snd_oss_root);
+               snd_info_free_entry(snd_oss_root);
 #endif
-               snd_remove_proc_entry(&proc_root, snd_proc_root);
+               snd_remove_proc_entry(NULL, snd_proc_root);
        }
        return 0;
 }
@@ -611,7 +601,8 @@ int snd_info_card_create(struct snd_card *card)
        char str[8];
        struct snd_info_entry *entry;
 
-       snd_assert(card != NULL, return -ENXIO);
+       if (snd_BUG_ON(!card))
+               return -ENXIO;
 
        sprintf(str, "card%i", card->number);
        if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
@@ -633,7 +624,8 @@ int snd_info_card_register(struct snd_card *card)
 {
        struct proc_dir_entry *p;
 
-       snd_assert(card != NULL, return -ENXIO);
+       if (snd_BUG_ON(!card))
+               return -ENXIO;
 
        if (!strcmp(card->id, card->proc_root->name))
                return 0;
@@ -646,20 +638,50 @@ int snd_info_card_register(struct snd_card *card)
 }
 
 /*
+ * called on card->id change
+ */
+void snd_info_card_id_change(struct snd_card *card)
+{
+       mutex_lock(&info_mutex);
+       if (card->proc_root_link) {
+               snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
+               card->proc_root_link = NULL;
+       }
+       if (strcmp(card->id, card->proc_root->name))
+               card->proc_root_link = proc_symlink(card->id,
+                                                   snd_proc_root,
+                                                   card->proc_root->name);
+       mutex_unlock(&info_mutex);
+}
+
+/*
  * de-register the card proc file
  * called from init.c
  */
-int snd_info_card_free(struct snd_card *card)
+void snd_info_card_disconnect(struct snd_card *card)
 {
-       snd_assert(card != NULL, return -ENXIO);
+       if (!card)
+               return;
+       mutex_lock(&info_mutex);
        if (card->proc_root_link) {
                snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
                card->proc_root_link = NULL;
        }
-       if (card->proc_root) {
-               snd_info_unregister(card->proc_root);
-               card->proc_root = NULL;
-       }
+       if (card->proc_root)
+               snd_info_disconnect(card->proc_root);
+       mutex_unlock(&info_mutex);
+}
+
+/*
+ * release the card proc file resources
+ * called from init.c
+ */
+int snd_info_card_free(struct snd_card *card)
+{
+       if (!card)
+               return 0;
+       snd_info_free_entry(card->proc_root);
+       card->proc_root = NULL;
        return 0;
 }
 
@@ -716,7 +738,7 @@ EXPORT_SYMBOL(snd_info_get_line);
  * Returns the updated pointer of the original string so that
  * it can be used for the next call.
  */
-char *snd_info_get_str(char *dest, char *src, int len)
+const char *snd_info_get_str(char *dest, const char *src, int len)
 {
        int c;
 
@@ -768,6 +790,8 @@ static struct snd_info_entry *snd_info_create_entry(const char *name)
        entry->mode = S_IFREG | S_IRUGO;
        entry->content = SNDRV_INFO_CONTENT_TEXT;
        mutex_init(&entry->access);
+       INIT_LIST_HEAD(&entry->children);
+       INIT_LIST_HEAD(&entry->list);
        return entry;
 }
 
@@ -820,30 +844,35 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
 
 EXPORT_SYMBOL(snd_info_create_card_entry);
 
-static int snd_info_dev_free_entry(struct snd_device *device)
+static void snd_info_disconnect(struct snd_info_entry *entry)
 {
-       struct snd_info_entry *entry = device->device_data;
-       snd_info_free_entry(entry);
-       return 0;
-}
+       struct list_head *p, *n;
+       struct proc_dir_entry *root;
 
-static int snd_info_dev_register_entry(struct snd_device *device)
-{
-       struct snd_info_entry *entry = device->device_data;
-       return snd_info_register(entry);
+       list_for_each_safe(p, n, &entry->children) {
+               snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
+       }
+
+       if (! entry->p)
+               return;
+       list_del_init(&entry->list);
+       root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+       snd_BUG_ON(!root);
+       snd_remove_proc_entry(root, entry->p);
+       entry->p = NULL;
 }
 
-static int snd_info_dev_disconnect_entry(struct snd_device *device)
+static int snd_info_dev_free_entry(struct snd_device *device)
 {
        struct snd_info_entry *entry = device->device_data;
-       entry->disconnected = 1;
+       snd_info_free_entry(entry);
        return 0;
 }
 
-static int snd_info_dev_unregister_entry(struct snd_device *device)
+static int snd_info_dev_register_entry(struct snd_device *device)
 {
        struct snd_info_entry *entry = device->device_data;
-       return snd_info_unregister(entry);
+       return snd_info_register(entry);
 }
 
 /**
@@ -872,8 +901,7 @@ int snd_card_proc_new(struct snd_card *card, const char *name,
        static struct snd_device_ops ops = {
                .dev_free = snd_info_dev_free_entry,
                .dev_register = snd_info_dev_register_entry,
-               .dev_disconnect = snd_info_dev_disconnect_entry,
-               .dev_unregister = snd_info_dev_unregister_entry
+               /* disconnect is done via snd_info_card_disconnect() */
        };
        struct snd_info_entry *entry;
        int err;
@@ -902,6 +930,11 @@ void snd_info_free_entry(struct snd_info_entry * entry)
 {
        if (entry == NULL)
                return;
+       if (entry->p) {
+               mutex_lock(&info_mutex);
+               snd_info_disconnect(entry);
+               mutex_unlock(&info_mutex);
+       }
        kfree(entry->name);
        if (entry->private_free)
                entry->private_free(entry);
@@ -922,57 +955,33 @@ int snd_info_register(struct snd_info_entry * entry)
 {
        struct proc_dir_entry *root, *p = NULL;
 
-       snd_assert(entry != NULL, return -ENXIO);
+       if (snd_BUG_ON(!entry))
+               return -ENXIO;
        root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
        mutex_lock(&info_mutex);
-       p = snd_create_proc_entry(entry->name, entry->mode, root);
+       p = create_proc_entry(entry->name, entry->mode, root);
        if (!p) {
                mutex_unlock(&info_mutex);
                return -ENOMEM;
        }
-       p->owner = entry->module;
        if (!S_ISDIR(entry->mode))
                p->proc_fops = &snd_info_entry_operations;
        p->size = entry->size;
        p->data = entry;
        entry->p = p;
+       if (entry->parent)
+               list_add_tail(&entry->list, &entry->parent->children);
        mutex_unlock(&info_mutex);
        return 0;
 }
 
 EXPORT_SYMBOL(snd_info_register);
 
-/**
- * snd_info_unregister - de-register the info entry
- * @entry: the info entry
- *
- * De-registers the info entry and releases the instance.
- *
- * Returns zero if successful, or a negative error code on failure.
- */
-int snd_info_unregister(struct snd_info_entry * entry)
-{
-       struct proc_dir_entry *root;
-
-       if (! entry)
-               return 0;
-       snd_assert(entry->p != NULL, return -ENXIO);
-       root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
-       snd_assert(root, return -ENXIO);
-       mutex_lock(&info_mutex);
-       snd_remove_proc_entry(root, entry->p);
-       mutex_unlock(&info_mutex);
-       snd_info_free_entry(entry);
-       return 0;
-}
-
-EXPORT_SYMBOL(snd_info_unregister);
-
 /*
 
  */
 
-static struct snd_info_entry *snd_info_version_entry = NULL;
+static struct snd_info_entry *snd_info_version_entry;
 
 static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 {
@@ -1000,8 +1009,7 @@ static int __init snd_info_version_init(void)
 
 static int __exit snd_info_version_done(void)
 {
-       if (snd_info_version_entry)
-               snd_info_unregister(snd_info_version_entry);
+       snd_info_free_entry(snd_info_version_entry);
        return 0;
 }