Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec
[cascardo/linux.git] / sound / core / compress_offload.c
index 9b3334b..2c49848 100644 (file)
@@ -67,6 +67,8 @@ struct snd_compr_file {
        struct snd_compr_stream stream;
 };
 
+static void error_delayed_work(struct work_struct *work);
+
 /*
  * a note on stream states used:
  * we use following states in the compressed core
@@ -123,6 +125,9 @@ static int snd_compr_open(struct inode *inode, struct file *f)
                snd_card_unref(compr->card);
                return -ENOMEM;
        }
+
+       INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
+
        data->stream.ops = compr->ops;
        data->stream.direction = dirn;
        data->stream.private_data = compr->private_data;
@@ -153,6 +158,8 @@ static int snd_compr_free(struct inode *inode, struct file *f)
        struct snd_compr_file *data = f->private_data;
        struct snd_compr_runtime *runtime = data->stream.runtime;
 
+       cancel_delayed_work_sync(&data->stream.error_work);
+
        switch (runtime->state) {
        case SNDRV_PCM_STATE_RUNNING:
        case SNDRV_PCM_STATE_DRAINING:
@@ -237,6 +244,15 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
        avail = snd_compr_calc_avail(stream, &ioctl_avail);
        ioctl_avail.avail = avail;
 
+       switch (stream->runtime->state) {
+       case SNDRV_PCM_STATE_OPEN:
+               return -EBADFD;
+       case SNDRV_PCM_STATE_XRUN:
+               return -EPIPE;
+       default:
+               break;
+       }
+
        if (copy_to_user((__u64 __user *)arg,
                                &ioctl_avail, sizeof(ioctl_avail)))
                return -EFAULT;
@@ -346,11 +362,13 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
        switch (stream->runtime->state) {
        case SNDRV_PCM_STATE_OPEN:
        case SNDRV_PCM_STATE_PREPARED:
-       case SNDRV_PCM_STATE_XRUN:
        case SNDRV_PCM_STATE_SUSPENDED:
        case SNDRV_PCM_STATE_DISCONNECTED:
                retval = -EBADFD;
                goto out;
+       case SNDRV_PCM_STATE_XRUN:
+               retval = -EPIPE;
+               goto out;
        }
 
        avail = snd_compr_get_avail(stream);
@@ -399,10 +417,16 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
        stream = &data->stream;
 
        mutex_lock(&stream->device->lock);
-       if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+
+       switch (stream->runtime->state) {
+       case SNDRV_PCM_STATE_OPEN:
+       case SNDRV_PCM_STATE_XRUN:
                retval = snd_compr_get_poll(stream) | POLLERR;
                goto out;
+       default:
+               break;
        }
+
        poll_wait(f, &stream->runtime->sleep, wait);
 
        avail = snd_compr_get_avail(stream);
@@ -697,6 +721,45 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
        return retval;
 }
 
+static void error_delayed_work(struct work_struct *work)
+{
+       struct snd_compr_stream *stream;
+
+       stream = container_of(work, struct snd_compr_stream, error_work.work);
+
+       mutex_lock(&stream->device->lock);
+
+       stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
+       wake_up(&stream->runtime->sleep);
+
+       mutex_unlock(&stream->device->lock);
+}
+
+/*
+ * snd_compr_stop_error: Report a fatal error on a stream
+ * @stream: pointer to stream
+ * @state: state to transition the stream to
+ *
+ * Stop the stream and set its state.
+ *
+ * Should be called with compressed device lock held.
+ */
+int snd_compr_stop_error(struct snd_compr_stream *stream,
+                        snd_pcm_state_t state)
+{
+       if (stream->runtime->state == state)
+               return 0;
+
+       stream->runtime->state = state;
+
+       pr_debug("Changing state to: %d\n", state);
+
+       queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_compr_stop_error);
+
 static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
 {
        int ret;