Merge branch 'linux-3.4.57' into rel-17
[linux-2.6.git] / sound / core / rawmidi.c
index d14dcbb..1bb95ae 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Abstract layer for MIDI v1.0 stream
- *  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 <sound/core.h>
 #include <linux/major.h>
 #include <linux/init.h>
-#include <linux/smp_lock.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/wait.h>
 #include <linux/mutex.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
 #include <linux/delay.h>
-#include <linux/wait.h>
 #include <sound/rawmidi.h>
 #include <sound/info.h>
 #include <sound/control.h>
 #include <sound/minors.h>
 #include <sound/initval.h>
 
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");
 MODULE_LICENSE("GPL");
 
@@ -95,16 +92,12 @@ static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substre
               (!substream->append || runtime->avail >= count);
 }
 
-static void snd_rawmidi_input_event_tasklet(unsigned long data)
-{
-       struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;
-       substream->runtime->event(substream);
-}
-
-static void snd_rawmidi_output_trigger_tasklet(unsigned long data)
+static void snd_rawmidi_input_event_work(struct work_struct *work)
 {
-       struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;
-       substream->ops->trigger(substream, 1);
+       struct snd_rawmidi_runtime *runtime =
+               container_of(work, struct snd_rawmidi_runtime, event_work);
+       if (runtime->event)
+               runtime->event(runtime->substream);
 }
 
 static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
@@ -113,16 +106,10 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
 
        if ((runtime = kzalloc(sizeof(*runtime), GFP_KERNEL)) == NULL)
                return -ENOMEM;
+       runtime->substream = substream;
        spin_lock_init(&runtime->lock);
        init_waitqueue_head(&runtime->sleep);
-       if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
-               tasklet_init(&runtime->tasklet,
-                            snd_rawmidi_input_event_tasklet,
-                            (unsigned long)substream);
-       else
-               tasklet_init(&runtime->tasklet,
-                            snd_rawmidi_output_trigger_tasklet,
-                            (unsigned long)substream);
+       INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
        runtime->event = NULL;
        runtime->buffer_size = PAGE_SIZE;
        runtime->avail_min = 1;
@@ -151,19 +138,18 @@ static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream)
 
 static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream,int up)
 {
-       if (up) {
-               tasklet_hi_schedule(&substream->runtime->tasklet);
-       } else {
-               tasklet_kill(&substream->runtime->tasklet);
-               substream->ops->trigger(substream, 0);
-       }
+       if (!substream->opened)
+               return;
+       substream->ops->trigger(substream, up);
 }
 
 static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
 {
+       if (!substream->opened)
+               return;
        substream->ops->trigger(substream, up);
-       if (!up && substream->runtime->event)
-               tasklet_kill(&substream->runtime->tasklet);
+       if (!up)
+               cancel_work_sync(&substream->runtime->event_work);
 }
 
 int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
@@ -223,156 +209,140 @@ int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)
        return 0;
 }
 
-int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
-                           int mode, struct snd_rawmidi_file * rfile)
+/* look for an available substream for the given stream direction;
+ * if a specific subdevice is given, try to assign it
+ */
+static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
+                           int stream, int mode,
+                           struct snd_rawmidi_substream **sub_ret)
 {
-       struct snd_rawmidi *rmidi;
-       struct list_head *list1, *list2;
-       struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
-       struct snd_rawmidi_runtime *input = NULL, *output = NULL;
-       int err;
+       struct snd_rawmidi_substream *substream;
+       struct snd_rawmidi_str *s = &rmidi->streams[stream];
+       static unsigned int info_flags[2] = {
+               [SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT,
+               [SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT,
+       };
 
-       if (rfile)
-               rfile->input = rfile->output = NULL;
-       mutex_lock(&register_mutex);
-       rmidi = snd_rawmidi_search(card, device);
-       mutex_unlock(&register_mutex);
-       if (rmidi == NULL) {
-               err = -ENODEV;
-               goto __error1;
-       }
-       if (!try_module_get(rmidi->card->module)) {
-               err = -EFAULT;
-               goto __error1;
-       }
-       if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
-               mutex_lock(&rmidi->open_mutex);
-       if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
-               if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) {
-                       err = -ENXIO;
-                       goto __error;
-               }
-               if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
-                       err = -ENODEV;
-                       goto __error;
-               }
-               if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >=
-                   rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
-                       err = -EAGAIN;
-                       goto __error;
-               }
-       }
-       if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
-               if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) {
-                       err = -ENXIO;
-                       goto __error;
-               }
-               if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
-                       err = -ENODEV;
-                       goto __error;
-               }
-               if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >=
-                   rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
-                       err = -EAGAIN;
-                       goto __error;
+       if (!(rmidi->info_flags & info_flags[stream]))
+               return -ENXIO;
+       if (subdevice >= 0 && subdevice >= s->substream_count)
+               return -ENODEV;
+
+       list_for_each_entry(substream, &s->substreams, list) {
+               if (substream->opened) {
+                       if (stream == SNDRV_RAWMIDI_STREAM_INPUT ||
+                           !(mode & SNDRV_RAWMIDI_LFLG_APPEND) ||
+                           !substream->append)
+                               continue;
                }
-       }
-       list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next;
-       while (1) {
-               if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
-                       sinput = NULL;
-                       if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
-                               err = -EAGAIN;
-                               goto __error;
-                       }
-                       break;
+               if (subdevice < 0 || subdevice == substream->number) {
+                       *sub_ret = substream;
+                       return 0;
                }
-               sinput = list_entry(list1, struct snd_rawmidi_substream, list);
-               if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened)
-                       goto __nexti;
-               if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number))
-                       break;
-             __nexti:
-               list1 = list1->next;
        }
-       list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next;
-       while (1) {
-               if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
-                       soutput = NULL;
-                       if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
-                               err = -EAGAIN;
-                               goto __error;
-                       }
-                       break;
-               }
-               soutput = list_entry(list2, struct snd_rawmidi_substream, list);
-               if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
-                       if (mode & SNDRV_RAWMIDI_LFLG_APPEND) {
-                               if (soutput->opened && !soutput->append)
-                                       goto __nexto;
-                       } else {
-                               if (soutput->opened)
-                                       goto __nexto;
-                       }
+       return -EAGAIN;
+}
+
+/* open and do ref-counting for the given substream */
+static int open_substream(struct snd_rawmidi *rmidi,
+                         struct snd_rawmidi_substream *substream,
+                         int mode)
+{
+       int err;
+
+       if (substream->use_count == 0) {
+               err = snd_rawmidi_runtime_create(substream);
+               if (err < 0)
+                       return err;
+               err = substream->ops->open(substream);
+               if (err < 0) {
+                       snd_rawmidi_runtime_free(substream);
+                       return err;
                }
-               if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number))
-                       break;
-             __nexto:
-               list2 = list2->next;
+               substream->opened = 1;
+               substream->active_sensing = 0;
+               if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
+                       substream->append = 1;
+               substream->pid = get_pid(task_pid(current));
+               rmidi->streams[substream->stream].substream_opened++;
        }
+       substream->use_count++;
+       return 0;
+}
+
+static void close_substream(struct snd_rawmidi *rmidi,
+                           struct snd_rawmidi_substream *substream,
+                           int cleanup);
+
+static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
+                            struct snd_rawmidi_file *rfile)
+{
+       struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
+       int err;
+
+       rfile->input = rfile->output = NULL;
        if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
-               if ((err = snd_rawmidi_runtime_create(sinput)) < 0)
-                       goto __error;
-               input = sinput->runtime;
-               if ((err = sinput->ops->open(sinput)) < 0)
-                       goto __error;
-               sinput->opened = 1;
-               rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++;
-       } else {
-               sinput = NULL;
+               err = assign_substream(rmidi, subdevice,
+                                      SNDRV_RAWMIDI_STREAM_INPUT,
+                                      mode, &sinput);
+               if (err < 0)
+                       return err;
        }
        if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
-               if (soutput->opened)
-                       goto __skip_output;
-               if ((err = snd_rawmidi_runtime_create(soutput)) < 0) {
-                       if (mode & SNDRV_RAWMIDI_LFLG_INPUT)
-                               sinput->ops->close(sinput);
-                       goto __error;
-               }
-               output = soutput->runtime;
-               if ((err = soutput->ops->open(soutput)) < 0) {
-                       if (mode & SNDRV_RAWMIDI_LFLG_INPUT)
-                               sinput->ops->close(sinput);
-                       goto __error;
-               }
-             __skip_output:
-               soutput->opened = 1;
-               if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
-                       soutput->append = 1;
-               if (soutput->use_count++ == 0)
-                       soutput->active_sensing = 1;
-               rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++;
-       } else {
-               soutput = NULL;
+               err = assign_substream(rmidi, subdevice,
+                                      SNDRV_RAWMIDI_STREAM_OUTPUT,
+                                      mode, &soutput);
+               if (err < 0)
+                       return err;
        }
-       if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
-               mutex_unlock(&rmidi->open_mutex);
-       if (rfile) {
-               rfile->rmidi = rmidi;
-               rfile->input = sinput;
-               rfile->output = soutput;
+
+       if (sinput) {
+               err = open_substream(rmidi, sinput, mode);
+               if (err < 0)
+                       return err;
+       }
+       if (soutput) {
+               err = open_substream(rmidi, soutput, mode);
+               if (err < 0) {
+                       if (sinput)
+                               close_substream(rmidi, sinput, 0);
+                       return err;
+               }
        }
+
+       rfile->rmidi = rmidi;
+       rfile->input = sinput;
+       rfile->output = soutput;
        return 0;
+}
 
-      __error:
-       if (input != NULL)
-               snd_rawmidi_runtime_free(sinput);
-       if (output != NULL)
-               snd_rawmidi_runtime_free(soutput);
-       module_put(rmidi->card->module);
-       if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
-               mutex_unlock(&rmidi->open_mutex);
-      __error1:
+/* called from sound/core/seq/seq_midi.c */
+int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
+                           int mode, struct snd_rawmidi_file * rfile)
+{
+       struct snd_rawmidi *rmidi;
+       int err;
+
+       if (snd_BUG_ON(!rfile))
+               return -EINVAL;
+
+       mutex_lock(&register_mutex);
+       rmidi = snd_rawmidi_search(card, device);
+       if (rmidi == NULL) {
+               mutex_unlock(&register_mutex);
+               return -ENODEV;
+       }
+       if (!try_module_get(rmidi->card->module)) {
+               mutex_unlock(&register_mutex);
+               return -ENXIO;
+       }
+       mutex_unlock(&register_mutex);
+
+       mutex_lock(&rmidi->open_mutex);
+       err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
+       mutex_unlock(&rmidi->open_mutex);
+       if (err < 0)
+               module_put(rmidi->card->module);
        return err;
 }
 
@@ -384,10 +354,17 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
        unsigned short fflags;
        int err;
        struct snd_rawmidi *rmidi;
-       struct snd_rawmidi_file *rawmidi_file;
+       struct snd_rawmidi_file *rawmidi_file = NULL;
        wait_queue_t wait;
        struct snd_ctl_file *kctl;
 
+       if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) 
+               return -EINVAL;         /* invalid combination */
+
+       err = nonseekable_open(inode, file);
+       if (err < 0)
+               return err;
+
        if (maj == snd_major) {
                rmidi = snd_lookup_minor_data(iminor(inode),
                                              SNDRV_DEVICE_TYPE_RAWMIDI);
@@ -401,37 +378,39 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
 
        if (rmidi == NULL)
                return -ENODEV;
-       if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) 
-               return -EINVAL;         /* invalid combination */
+
+       if (!try_module_get(rmidi->card->module)) {
+               snd_card_unref(rmidi->card);
+               return -ENXIO;
+       }
+
+       mutex_lock(&rmidi->open_mutex);
        card = rmidi->card;
        err = snd_card_file_add(card, file);
        if (err < 0)
-               return -ENODEV;
+               goto __error_card;
        fflags = snd_rawmidi_file_flags(file);
        if ((file->f_flags & O_APPEND) || maj == SOUND_MAJOR) /* OSS emul? */
                fflags |= SNDRV_RAWMIDI_LFLG_APPEND;
-       fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK;
        rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL);
        if (rawmidi_file == NULL) {
-               snd_card_file_remove(card, file);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto __error;
        }
        init_waitqueue_entry(&wait, current);
        add_wait_queue(&rmidi->open_wait, &wait);
-       mutex_lock(&rmidi->open_mutex);
        while (1) {
                subdevice = -1;
-               down_read(&card->controls_rwsem);
+               read_lock(&card->ctl_files_rwlock);
                list_for_each_entry(kctl, &card->ctl_files, list) {
-                       if (kctl->pid == current->pid) {
+                       if (kctl->pid == task_pid(current)) {
                                subdevice = kctl->prefer_rawmidi_subdevice;
                                if (subdevice != -1)
                                        break;
                        }
                }
-               up_read(&card->controls_rwsem);
-               err = snd_rawmidi_kernel_open(rmidi->card, rmidi->device,
-                                             subdevice, fflags, rawmidi_file);
+               read_unlock(&card->ctl_files_rwlock);
+               err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file);
                if (err >= 0)
                        break;
                if (err == -EAGAIN) {
@@ -445,72 +424,102 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
                mutex_unlock(&rmidi->open_mutex);
                schedule();
                mutex_lock(&rmidi->open_mutex);
+               if (rmidi->card->shutdown) {
+                       err = -ENODEV;
+                       break;
+               }
                if (signal_pending(current)) {
                        err = -ERESTARTSYS;
                        break;
                }
        }
+       remove_wait_queue(&rmidi->open_wait, &wait);
+       if (err < 0) {
+               kfree(rawmidi_file);
+               goto __error;
+       }
 #ifdef CONFIG_SND_OSSEMUL
        if (rawmidi_file->input && rawmidi_file->input->runtime)
                rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR);
        if (rawmidi_file->output && rawmidi_file->output->runtime)
                rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR);
 #endif
-       remove_wait_queue(&rmidi->open_wait, &wait);
-       if (err >= 0) {
-               file->private_data = rawmidi_file;
-       } else {
-               snd_card_file_remove(card, file);
-               kfree(rawmidi_file);
-       }
+       file->private_data = rawmidi_file;
+       mutex_unlock(&rmidi->open_mutex);
+       snd_card_unref(rmidi->card);
+       return 0;
+
+ __error:
+       snd_card_file_remove(card, file);
+ __error_card:
        mutex_unlock(&rmidi->open_mutex);
+       module_put(rmidi->card->module);
+       snd_card_unref(rmidi->card);
        return err;
 }
 
-int snd_rawmidi_kernel_release(struct snd_rawmidi_file * rfile)
+static void close_substream(struct snd_rawmidi *rmidi,
+                           struct snd_rawmidi_substream *substream,
+                           int cleanup)
 {
-       struct snd_rawmidi *rmidi;
-       struct snd_rawmidi_substream *substream;
-       struct snd_rawmidi_runtime *runtime;
+       if (--substream->use_count)
+               return;
 
-       snd_assert(rfile != NULL, return -ENXIO);
-       snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO);
-       rmidi = rfile->rmidi;
-       mutex_lock(&rmidi->open_mutex);
-       if (rfile->input != NULL) {
-               substream = rfile->input;
-               rfile->input = NULL;
-               runtime = substream->runtime;
-               snd_rawmidi_input_trigger(substream, 0);
-               substream->ops->close(substream);
-               if (runtime->private_free != NULL)
-                       runtime->private_free(substream);
-               snd_rawmidi_runtime_free(substream);
-               substream->opened = 0;
-               rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--;
-       }
-       if (rfile->output != NULL) {
-               substream = rfile->output;
-               rfile->output = NULL;
-               if (--substream->use_count == 0) {
-                       runtime = substream->runtime;
+       if (cleanup) {
+               if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
+                       snd_rawmidi_input_trigger(substream, 0);
+               else {
                        if (substream->active_sensing) {
                                unsigned char buf = 0xfe;
-                               /* sending single active sensing message to shut the device up */
+                               /* sending single active sensing message
+                                * to shut the device up
+                                */
                                snd_rawmidi_kernel_write(substream, &buf, 1);
                        }
                        if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
                                snd_rawmidi_output_trigger(substream, 0);
-                       substream->ops->close(substream);
-                       if (runtime->private_free != NULL)
-                               runtime->private_free(substream);
-                       snd_rawmidi_runtime_free(substream);
-                       substream->opened = 0;
-                       substream->append = 0;
                }
-               rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--;
        }
+       substream->ops->close(substream);
+       if (substream->runtime->private_free)
+               substream->runtime->private_free(substream);
+       snd_rawmidi_runtime_free(substream);
+       substream->opened = 0;
+       substream->append = 0;
+       put_pid(substream->pid);
+       substream->pid = NULL;
+       rmidi->streams[substream->stream].substream_opened--;
+}
+
+static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
+{
+       struct snd_rawmidi *rmidi;
+
+       rmidi = rfile->rmidi;
+       mutex_lock(&rmidi->open_mutex);
+       if (rfile->input) {
+               close_substream(rmidi, rfile->input, 1);
+               rfile->input = NULL;
+       }
+       if (rfile->output) {
+               close_substream(rmidi, rfile->output, 1);
+               rfile->output = NULL;
+       }
+       rfile->rmidi = NULL;
        mutex_unlock(&rmidi->open_mutex);
+       wake_up(&rmidi->open_wait);
+}
+
+/* called from sound/core/seq/seq_midi.c */
+int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile)
+{
+       struct snd_rawmidi *rmidi;
+
+       if (snd_BUG_ON(!rfile))
+               return -ENXIO;
+       
+       rmidi = rfile->rmidi;
+       rawmidi_release_priv(rfile);
        module_put(rmidi->card->module);
        return 0;
 }
@@ -519,15 +528,16 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file)
 {
        struct snd_rawmidi_file *rfile;
        struct snd_rawmidi *rmidi;
-       int err;
+       struct module *module;
 
        rfile = file->private_data;
-       err = snd_rawmidi_kernel_release(rfile);
        rmidi = rfile->rmidi;
-       wake_up(&rmidi->open_wait);
+       rawmidi_release_priv(rfile);
        kfree(rfile);
+       module = rmidi->card->module;
        snd_card_file_remove(rmidi->card, file);
-       return err;
+       module_put(module);
+       return 0;
 }
 
 static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
@@ -624,10 +634,10 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
                return -EINVAL;
        }
        if (params->buffer_size != runtime->buffer_size) {
-               newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
+               newbuf = krealloc(runtime->buffer, params->buffer_size,
+                                 GFP_KERNEL);
                if (!newbuf)
                        return -ENOMEM;
-               kfree(runtime->buffer);
                runtime->buffer = newbuf;
                runtime->buffer_size = params->buffer_size;
                runtime->avail = runtime->buffer_size;
@@ -651,10 +661,10 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
                return -EINVAL;
        }
        if (params->buffer_size != runtime->buffer_size) {
-               newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
+               newbuf = krealloc(runtime->buffer, params->buffer_size,
+                                 GFP_KERNEL);
                if (!newbuf)
                        return -ENOMEM;
-               kfree(runtime->buffer);
                runtime->buffer = newbuf;
                runtime->buffer_size = params->buffer_size;
        }
@@ -814,6 +824,8 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
                
                if (get_user(device, (int __user *)argp))
                        return -EFAULT;
+               if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
+                       device = SNDRV_RAWMIDI_DEVICES - 1;
                mutex_lock(&register_mutex);
                device = device < 0 ? 0 : device + 1;
                while (device < SNDRV_RAWMIDI_DEVICES) {
@@ -860,6 +872,8 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
        int result = 0, count1;
        struct snd_rawmidi_runtime *runtime = substream->runtime;
 
+       if (!substream->opened)
+               return -EBADFD;
        if (runtime->buffer == NULL) {
                snd_printd("snd_rawmidi_receive: input is not active!!!\n");
                return -EINVAL;
@@ -905,7 +919,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
        }
        if (result > 0) {
                if (runtime->event)
-                       tasklet_hi_schedule(&runtime->tasklet);
+                       schedule_work(&runtime->event_work);
                else if (snd_rawmidi_ready(substream))
                        wake_up(&runtime->sleep);
        }
@@ -914,7 +928,8 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
 }
 
 static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
-                                    unsigned char *buf, long count, int kernel)
+                                    unsigned char __user *userbuf,
+                                    unsigned char *kernelbuf, long count)
 {
        unsigned long flags;
        long result = 0, count1;
@@ -927,11 +942,11 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
                spin_lock_irqsave(&runtime->lock, flags);
                if (count1 > (int)runtime->avail)
                        count1 = runtime->avail;
-               if (kernel) {
-                       memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1);
-               } else {
+               if (kernelbuf)
+                       memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
+               if (userbuf) {
                        spin_unlock_irqrestore(&runtime->lock, flags);
-                       if (copy_to_user((char __user *)buf + result,
+                       if (copy_to_user(userbuf + result,
                                         runtime->buffer + runtime->appl_ptr, count1)) {
                                return result > 0 ? result : -EFAULT;
                        }
@@ -951,7 +966,7 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
                             unsigned char *buf, long count)
 {
        snd_rawmidi_input_trigger(substream, 1);
-       return snd_rawmidi_kernel_read1(substream, buf, count, 1);
+       return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count);
 }
 
 static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count,
@@ -984,6 +999,8 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
                        spin_unlock_irq(&runtime->lock);
                        schedule();
                        remove_wait_queue(&runtime->sleep, &wait);
+                       if (rfile->rmidi->card->shutdown)
+                               return -ENODEV;
                        if (signal_pending(current))
                                return result > 0 ? result : -ERESTARTSYS;
                        if (!runtime->avail)
@@ -992,8 +1009,9 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
                }
                spin_unlock_irq(&runtime->lock);
                count1 = snd_rawmidi_kernel_read1(substream,
-                                                 (unsigned char __force *)buf,
-                                                 count, 0);
+                                                 (unsigned char __user *)buf,
+                                                 NULL/*kernelbuf*/,
+                                                 count);
                if (count1 < 0)
                        return result > 0 ? result : count1;
                result += count1;
@@ -1101,7 +1119,7 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
                return -EINVAL;
        }
        spin_lock_irqsave(&runtime->lock, flags);
-       snd_assert(runtime->avail + count <= runtime->buffer_size, );
+       snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
        runtime->hw_ptr += count;
        runtime->hw_ptr %= runtime->buffer_size;
        runtime->avail += count;
@@ -1127,6 +1145,8 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
 int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
                         unsigned char *buffer, int count)
 {
+       if (!substream->opened)
+               return -EBADFD;
        count = snd_rawmidi_transmit_peek(substream, buffer, count);
        if (count < 0)
                return count;
@@ -1134,14 +1154,18 @@ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
 }
 
 static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
-                                     const unsigned char *buf, long count, int kernel)
+                                     const unsigned char __user *userbuf,
+                                     const unsigned char *kernelbuf,
+                                     long count)
 {
        unsigned long flags;
        long count1, result;
        struct snd_rawmidi_runtime *runtime = substream->runtime;
 
-       snd_assert(buf != NULL, return -EINVAL);
-       snd_assert(runtime->buffer != NULL, return -EINVAL);
+       if (snd_BUG_ON(!kernelbuf && !userbuf))
+               return -EINVAL;
+       if (snd_BUG_ON(!runtime->buffer))
+               return -EINVAL;
 
        result = 0;
        spin_lock_irqsave(&runtime->lock, flags);
@@ -1157,12 +1181,13 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
                        count1 = count;
                if (count1 > (long)runtime->avail)
                        count1 = runtime->avail;
-               if (kernel) {
-                       memcpy(runtime->buffer + runtime->appl_ptr, buf, count1);
-               } else {
+               if (kernelbuf)
+                       memcpy(runtime->buffer + runtime->appl_ptr,
+                              kernelbuf + result, count1);
+               else if (userbuf) {
                        spin_unlock_irqrestore(&runtime->lock, flags);
                        if (copy_from_user(runtime->buffer + runtime->appl_ptr,
-                                          (char __user *)buf, count1)) {
+                                          userbuf + result, count1)) {
                                spin_lock_irqsave(&runtime->lock, flags);
                                result = result > 0 ? result : -EFAULT;
                                goto __end;
@@ -1173,7 +1198,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
                runtime->appl_ptr %= runtime->buffer_size;
                runtime->avail -= count1;
                result += count1;
-               buf += count1;
                count -= count1;
        }
       __end:
@@ -1187,7 +1211,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
 long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
                              const unsigned char *buf, long count)
 {
-       return snd_rawmidi_kernel_write1(substream, buf, count, 1);
+       return snd_rawmidi_kernel_write1(substream, NULL, buf, count);
 }
 
 static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
@@ -1220,6 +1244,8 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
                        spin_unlock_irq(&runtime->lock);
                        timeout = schedule_timeout(30 * HZ);
                        remove_wait_queue(&runtime->sleep, &wait);
+                       if (rfile->rmidi->card->shutdown)
+                               return -ENODEV;
                        if (signal_pending(current))
                                return result > 0 ? result : -ERESTARTSYS;
                        if (!runtime->avail && !timeout)
@@ -1227,9 +1253,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
                        spin_lock_irq(&runtime->lock);
                }
                spin_unlock_irq(&runtime->lock);
-               count1 = snd_rawmidi_kernel_write1(substream,
-                                                  (unsigned char __force *)buf,
-                                                  count, 0);
+               count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
                if (count1 < 0)
                        return result > 0 ? result : count1;
                result += count1;
@@ -1238,7 +1262,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
                        break;
                count -= count1;
        }
-       if (file->f_flags & O_SYNC) {
+       if (file->f_flags & O_DSYNC) {
                spin_lock_irq(&runtime->lock);
                while (runtime->avail != runtime->buffer_size) {
                        wait_queue_t wait;
@@ -1320,6 +1344,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
                                    substream->number,
                                    (unsigned long) substream->bytes);
                        if (substream->opened) {
+                               snd_iprintf(buffer,
+                                   "  Owner PID    : %d\n",
+                                   pid_vnr(substream->pid));
                                runtime = substream->runtime;
                                snd_iprintf(buffer,
                                    "  Mode         : %s\n"
@@ -1341,6 +1368,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
                                    substream->number,
                                    (unsigned long) substream->bytes);
                        if (substream->opened) {
+                               snd_iprintf(buffer,
+                                           "  Owner PID    : %d\n",
+                                           pid_vnr(substream->pid));
                                runtime = substream->runtime;
                                snd_iprintf(buffer,
                                            "  Buffer size  : %lu\n"
@@ -1366,6 +1396,7 @@ static const struct file_operations snd_rawmidi_f_ops =
        .write =        snd_rawmidi_write,
        .open =         snd_rawmidi_open,
        .release =      snd_rawmidi_release,
+       .llseek =       no_llseek,
        .poll =         snd_rawmidi_poll,
        .unlocked_ioctl =       snd_rawmidi_ioctl,
        .compat_ioctl = snd_rawmidi_ioctl_compat,
@@ -1421,9 +1452,10 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
                .dev_disconnect = snd_rawmidi_dev_disconnect,
        };
 
-       snd_assert(rrawmidi != NULL, return -EINVAL);
-       *rrawmidi = NULL;
-       snd_assert(card != NULL, return -ENXIO);
+       if (snd_BUG_ON(!card))
+               return -ENXIO;
+       if (rrawmidi)
+               *rrawmidi = NULL;
        rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
        if (rmidi == NULL) {
                snd_printk(KERN_ERR "rawmidi: cannot allocate\n");
@@ -1456,7 +1488,8 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
                snd_rawmidi_free(rmidi);
                return err;
        }
-       *rrawmidi = rmidi;
+       if (rrawmidi)
+               *rrawmidi = rmidi;
        return 0;
 }
 
@@ -1473,7 +1506,8 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
 
 static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
 {
-       snd_assert(rmidi != NULL, return -ENXIO);       
+       if (!rmidi)
+               return 0;
 
        snd_info_free_entry(rmidi->proc_entry);
        rmidi->proc_entry = NULL;
@@ -1587,9 +1621,20 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
 static int snd_rawmidi_dev_disconnect(struct snd_device *device)
 {
        struct snd_rawmidi *rmidi = device->device_data;
+       int dir;
 
        mutex_lock(&register_mutex);
+       mutex_lock(&rmidi->open_mutex);
+       wake_up(&rmidi->open_wait);
        list_del_init(&rmidi->list);
+       for (dir = 0; dir < 2; dir++) {
+               struct snd_rawmidi_substream *s;
+               list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
+                       if (s->runtime)
+                               wake_up(&s->runtime->sleep);
+               }
+       }
+
 #ifdef CONFIG_SND_OSSEMUL
        if (rmidi->ossreg) {
                if ((int)rmidi->device == midi_map[rmidi->card->number]) {
@@ -1604,6 +1649,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
        }
 #endif /* CONFIG_SND_OSSEMUL */
        snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
+       mutex_unlock(&rmidi->open_mutex);
        mutex_unlock(&register_mutex);
        return 0;
 }