Merge branch 'topic/pcm-delay' into for-linus
[safe/jmp/linux-2.6] / sound / core / pcm_native.c
index df2299a..45dc53f 100644 (file)
@@ -186,7 +186,7 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
                if (!(params->rmask & (1 << k)))
                        continue;
 #ifdef RULES_DEBUG
-               printk("%s = ", snd_pcm_hw_param_names[k]);
+               printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]);
                printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
 #endif
                changed = snd_mask_refine(m, constrs_mask(constrs, k));
@@ -206,7 +206,7 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
                if (!(params->rmask & (1 << k)))
                        continue;
 #ifdef RULES_DEBUG
-               printk("%s = ", snd_pcm_hw_param_names[k]);
+               printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]);
                if (i->empty)
                        printk("empty");
                else
@@ -251,7 +251,7 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
                        if (!doit)
                                continue;
 #ifdef RULES_DEBUG
-                       printk("Rule %d [%p]: ", k, r->func);
+                       printk(KERN_DEBUG "Rule %d [%p]: ", k, r->func);
                        if (r->var >= 0) {
                                printk("%s = ", snd_pcm_hw_param_names[r->var]);
                                if (hw_is_mask(r->var)) {
@@ -327,21 +327,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params;
        int err;
 
-       params = kmalloc(sizeof(*params), GFP_KERNEL);
-       if (!params) {
-               err = -ENOMEM;
-               goto out;
-       }
-       if (copy_from_user(params, _params, sizeof(*params))) {
-               err = -EFAULT;
-               goto out;
-       }
+       params = memdup_user(_params, sizeof(*params));
+       if (IS_ERR(params))
+               return PTR_ERR(params);
+
        err = snd_pcm_hw_refine(substream, params);
        if (copy_to_user(_params, params, sizeof(*params))) {
                if (!err)
                        err = -EFAULT;
        }
-out:
+
        kfree(params);
        return err;
 }
@@ -465,21 +460,16 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params;
        int err;
 
-       params = kmalloc(sizeof(*params), GFP_KERNEL);
-       if (!params) {
-               err = -ENOMEM;
-               goto out;
-       }
-       if (copy_from_user(params, _params, sizeof(*params))) {
-               err = -EFAULT;
-               goto out;
-       }
+       params = memdup_user(_params, sizeof(*params));
+       if (IS_ERR(params))
+               return PTR_ERR(params);
+
        err = snd_pcm_hw_params(substream, params);
        if (copy_to_user(_params, params, sizeof(*params))) {
                if (!err)
                        err = -EFAULT;
        }
-out:
+
        kfree(params);
        return err;
 }
@@ -597,14 +587,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                status->avail = snd_pcm_playback_avail(runtime);
                if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
-                   runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+                   runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
                        status->delay = runtime->buffer_size - status->avail;
-               else
+                       status->delay += runtime->delay;
+               } else
                        status->delay = 0;
        } else {
                status->avail = snd_pcm_capture_avail(runtime);
                if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
-                       status->delay = status->avail;
+                       status->delay = status->avail + runtime->delay;
                else
                        status->delay = 0;
        }
@@ -858,6 +849,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_trigger_tstamp(substream);
+       runtime->hw_ptr_jiffies = jiffies;
        runtime->status->state = state;
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
@@ -875,10 +867,8 @@ static struct action_ops snd_pcm_action_start = {
 };
 
 /**
- * snd_pcm_start
+ * snd_pcm_start - start all linked streams
  * @substream: the PCM substream instance
- *
- * Start all linked streams.
  */
 int snd_pcm_start(struct snd_pcm_substream *substream)
 {
@@ -926,12 +916,11 @@ static struct action_ops snd_pcm_action_stop = {
 };
 
 /**
- * snd_pcm_stop
+ * snd_pcm_stop - try to stop all running streams in the substream group
  * @substream: the PCM substream instance
  * @state: PCM state after stopping the stream
  *
- * Try to stop all running streams in the substream group.
- * The state of each stream is changed to the given value after that unconditionally.
+ * The state of each stream is then changed to the given state unconditionally.
  */
 int snd_pcm_stop(struct snd_pcm_substream *substream, int state)
 {
@@ -941,11 +930,10 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, int state)
 EXPORT_SYMBOL(snd_pcm_stop);
 
 /**
- * snd_pcm_drain_done
+ * snd_pcm_drain_done - stop the DMA only when the given stream is playback
  * @substream: the PCM substream
  *
- * Stop the DMA only when the given stream is playback.
- * The state is changed to SETUP.
+ * After stopping, the state is changed to SETUP.
  * Unlike snd_pcm_stop(), this affects only the given stream.
  */
 int snd_pcm_drain_done(struct snd_pcm_substream *substream)
@@ -975,6 +963,11 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push)
 {
        if (substream->runtime->trigger_master != substream)
                return 0;
+       /* The jiffies check in snd_pcm_update_hw_ptr*() is done by
+        * a delta betwen the current jiffies, this gives a large enough
+        * delta, effectively to skip the check once.
+        */
+       substream->runtime->hw_ptr_jiffies = jiffies - HZ * 1000;
        return substream->ops->trigger(substream,
                                       push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH :
                                              SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
@@ -1065,10 +1058,9 @@ static struct action_ops snd_pcm_action_suspend = {
 };
 
 /**
- * snd_pcm_suspend
+ * snd_pcm_suspend - trigger SUSPEND to all linked streams
  * @substream: the PCM substream
  *
- * Trigger SUSPEND to all linked streams.
  * After this call, all streams are changed to SUSPENDED state.
  */
 int snd_pcm_suspend(struct snd_pcm_substream *substream)
@@ -1088,10 +1080,9 @@ int snd_pcm_suspend(struct snd_pcm_substream *substream)
 EXPORT_SYMBOL(snd_pcm_suspend);
 
 /**
- * snd_pcm_suspend_all
+ * snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
  * @pcm: the PCM instance
  *
- * Trigger SUSPEND to all substreams in the given pcm.
  * After this call, all streams are changed to SUSPENDED state.
  */
 int snd_pcm_suspend_all(struct snd_pcm *pcm)
@@ -1313,11 +1304,9 @@ static struct action_ops snd_pcm_action_prepare = {
 };
 
 /**
- * snd_pcm_prepare
+ * snd_pcm_prepare - prepare the PCM substream to be triggerable
  * @substream: the PCM substream instance
  * @file: file to refer f_flags
- *
- * Prepare the PCM substream to be triggerable.
  */
 static int snd_pcm_prepare(struct snd_pcm_substream *substream,
                           struct file *file)
@@ -2177,7 +2166,6 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
        if (snd_BUG_ON(!substream))
                return -ENXIO;
        pcm = substream->pcm;
-       fasync_helper(-1, file, 0, &substream->runtime->fasync);
        mutex_lock(&pcm->open_mutex);
        snd_pcm_release_substream(substream);
        kfree(pcm_file);
@@ -2423,6 +2411,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream,
                        n = snd_pcm_playback_hw_avail(runtime);
                else
                        n = snd_pcm_capture_avail(runtime);
+               n += runtime->delay;
                break;
        case SNDRV_PCM_STATE_XRUN:
                err = -EPIPE;
@@ -2602,13 +2591,11 @@ static int snd_pcm_playback_ioctl1(struct file *file,
                        return -EFAULT;
                if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
                        return -EFAULT;
-               bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL);
-               if (bufs == NULL)
-                       return -ENOMEM;
-               if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) {
-                       kfree(bufs);
-                       return -EFAULT;
-               }
+
+               bufs = memdup_user(xfern.bufs,
+                                  sizeof(void *) * runtime->channels);
+               if (IS_ERR(bufs))
+                       return PTR_ERR(bufs);
                result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
                kfree(bufs);
                __put_user(result, &_xfern->result);
@@ -2684,13 +2671,11 @@ static int snd_pcm_capture_ioctl1(struct file *file,
                        return -EFAULT;
                if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
                        return -EFAULT;
-               bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL);
-               if (bufs == NULL)
-                       return -ENOMEM;
-               if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) {
-                       kfree(bufs);
-                       return -EFAULT;
-               }
+
+               bufs = memdup_user(xfern.bufs,
+                                  sizeof(void *) * runtime->channels);
+               if (IS_ERR(bufs))
+                       return PTR_ERR(bufs);
                result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
                kfree(bufs);
                __put_user(result, &_xfern->result);
@@ -3255,9 +3240,7 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)
        err = fasync_helper(fd, file, on, &runtime->fasync);
 out:
        unlock_kernel();
-       if (err < 0)
-               return err;
-       return 0;
+       return err;
 }
 
 /*
@@ -3323,18 +3306,12 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
        int err;
 
        params = kmalloc(sizeof(*params), GFP_KERNEL);
-       if (!params) {
-               err = -ENOMEM;
-               goto out;
-       }
-       oparams = kmalloc(sizeof(*oparams), GFP_KERNEL);
-       if (!oparams) {
-               err = -ENOMEM;
-               goto out;
-       }
+       if (!params)
+               return -ENOMEM;
 
-       if (copy_from_user(oparams, _oparams, sizeof(*oparams))) {
-               err = -EFAULT;
+       oparams = memdup_user(_oparams, sizeof(*oparams));
+       if (IS_ERR(oparams)) {
+               err = PTR_ERR(oparams);
                goto out;
        }
        snd_pcm_hw_convert_from_old_params(params, oparams);
@@ -3344,9 +3321,10 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
                if (!err)
                        err = -EFAULT;
        }
+
+       kfree(oparams);
 out:
        kfree(params);
-       kfree(oparams);
        return err;
 }
 
@@ -3358,17 +3336,12 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
        int err;
 
        params = kmalloc(sizeof(*params), GFP_KERNEL);
-       if (!params) {
-               err = -ENOMEM;
-               goto out;
-       }
-       oparams = kmalloc(sizeof(*oparams), GFP_KERNEL);
-       if (!oparams) {
-               err = -ENOMEM;
-               goto out;
-       }
-       if (copy_from_user(oparams, _oparams, sizeof(*oparams))) {
-               err = -EFAULT;
+       if (!params)
+               return -ENOMEM;
+
+       oparams = memdup_user(_oparams, sizeof(*oparams));
+       if (IS_ERR(oparams)) {
+               err = PTR_ERR(oparams);
                goto out;
        }
        snd_pcm_hw_convert_from_old_params(params, oparams);
@@ -3378,13 +3351,25 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
                if (!err)
                        err = -EFAULT;
        }
+
+       kfree(oparams);
 out:
        kfree(params);
-       kfree(oparams);
        return err;
 }
 #endif /* CONFIG_SND_SUPPORT_OLD_API */
 
+#ifndef CONFIG_MMU
+unsigned long dummy_get_unmapped_area(struct file *file, unsigned long addr,
+                                     unsigned long len, unsigned long pgoff,
+                                     unsigned long flags)
+{
+       return 0;
+}
+#else
+# define dummy_get_unmapped_area NULL
+#endif
+
 /*
  *  Register section
  */
@@ -3401,6 +3386,7 @@ const struct file_operations snd_pcm_f_ops[2] = {
                .compat_ioctl =         snd_pcm_ioctl_compat,
                .mmap =                 snd_pcm_mmap,
                .fasync =               snd_pcm_fasync,
+               .get_unmapped_area =    dummy_get_unmapped_area,
        },
        {
                .owner =                THIS_MODULE,
@@ -3413,5 +3399,6 @@ const struct file_operations snd_pcm_f_ops[2] = {
                .compat_ioctl =         snd_pcm_ioctl_compat,
                .mmap =                 snd_pcm_mmap,
                .fasync =               snd_pcm_fasync,
+               .get_unmapped_area =    dummy_get_unmapped_area,
        }
 };