Alsa-Devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Takashi Iwai <tiwai@suse.de>
To: Giuliano Pochini <pochini@shiny.it>
Cc: alsa-devel@lists.sourceforge.net
Subject: Re: [PATCH] Kernel crash
Date: Thu, 16 Sep 2004 13:16:41 +0200	[thread overview]
Message-ID: <s5hacvqsbty.wl@alsa2.suse.de> (raw)
In-Reply-To: <s5hhdpzs968.wl@alsa2.suse.de>

[-- Attachment #1: Type: text/plain, Size: 700 bytes --]

At Wed, 15 Sep 2004 20:01:51 +0200,
I wrote:
> 
> I think it'd be better to add a new field to substream to indicate the
> DMA running status so that snd_pcm_stop() can be called safely
> regardless of the current status.
> 
> The situation right now is a mess, the DMA running state depending on
> the stream direction and the state.

We have a snd_pcm_running() so the flag is not always necessary
(although it would be nice).

In the current code, the PCM state is changed somehow unconditionally
among all linked substreams via snd_pcm_change_state(), and apparently
this causes the confusion.

I did clean up and rewrite of pcm core stuff.
Could you check the attached patch?


thanks,

Takashi

[-- Attachment #2: Type: text/plain, Size: 31678 bytes --]

Index: alsa-kernel/core/pcm_lib.c
===================================================================
RCS file: /home/tiwai/cvs/alsa/alsa-kernel/core/pcm_lib.c,v
retrieving revision 1.56
diff -u -r1.56 pcm_lib.c
--- alsa-kernel/core/pcm_lib.c	27 Jul 2004 11:20:08 -0000	1.56
+++ alsa-kernel/core/pcm_lib.c	15 Sep 2004 23:04:17 -0000
@@ -176,7 +176,7 @@
 		runtime->avail_max = avail;
 	if (avail >= runtime->stop_threshold) {
 		if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING)
-			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+			snd_pcm_drain_done(substream);
 		else
 			xrun(substream);
 		return -EPIPE;
Index: alsa-kernel/core/pcm_native.c
===================================================================
RCS file: /home/tiwai/cvs/alsa/alsa-kernel/core/pcm_native.c,v
retrieving revision 1.83
diff -u -r1.83 pcm_native.c
--- alsa-kernel/core/pcm_native.c	15 Sep 2004 09:04:26 -0000	1.83
+++ alsa-kernel/core/pcm_native.c	16 Sep 2004 00:49:49 -0000
@@ -451,7 +451,7 @@
 static int snd_pcm_hw_free(snd_pcm_substream_t * substream)
 {
 	snd_pcm_runtime_t *runtime;
-	int result;
+	int result = 0;
 
 	snd_assert(substream != NULL, return -ENXIO);
 	runtime = substream->runtime;
@@ -468,11 +468,8 @@
 	snd_pcm_stream_unlock_irq(substream);
 	if (atomic_read(&runtime->mmap_count))
 		return -EBADFD;
-	if (substream->ops->hw_free == NULL) {
-		runtime->status->state = SNDRV_PCM_STATE_OPEN;
-		return 0;
-	}
-	result = substream->ops->hw_free(substream);
+	if (substream->ops->hw_free)
+		result = substream->ops->hw_free(substream);
 	runtime->status->state = SNDRV_PCM_STATE_OPEN;
 	return result;
 }
@@ -652,6 +649,7 @@
 struct action_ops {
 	int (*pre_action)(snd_pcm_substream_t *substream, int state);
 	int (*do_action)(snd_pcm_substream_t *substream, int state);
+	void (*undo_action)(snd_pcm_substream_t *substream, int state);
 	void (*post_action)(snd_pcm_substream_t *substream, int state);
 };
 
@@ -666,7 +664,8 @@
 {
 	struct list_head *pos;
 	snd_pcm_substream_t *s = NULL;
-	int err, res = 0;
+	snd_pcm_substream_t *s1;
+	int res = 0;
 
 	snd_pcm_group_for_each(pos, substream) {
 		s = snd_pcm_group_substream_entry(pos);
@@ -674,24 +673,31 @@
 			spin_lock(&s->self_group.lock);
 		res = ops->pre_action(s, state);
 		if (res < 0)
-			break;
+			goto _unlock;
 	}
-	if (res >= 0) {
-		snd_pcm_group_for_each(pos, substream) {
-			s = snd_pcm_group_substream_entry(pos);
-			err = ops->do_action(s, state);
-			if (err < 0) {
-				if (res == 0)
-					res = err;
-			} else {
-				ops->post_action(s, state);
+	snd_pcm_group_for_each(pos, substream) {
+		s = snd_pcm_group_substream_entry(pos);
+		res = ops->do_action(s, state);
+		if (res < 0) {
+			if (ops->undo_action) {
+				snd_pcm_group_for_each(pos, substream) {
+					s1 = snd_pcm_group_substream_entry(pos);
+					if (s1 == s) /* failed stream */
+						break;
+					ops->undo_action(s1, state);
+				}
 			}
-			if (do_lock && s != substream)
-				spin_unlock(&s->self_group.lock);
+			s = NULL; /* unlock all */
+			goto _unlock;
 		}
-	} else if (do_lock) {
-		snd_pcm_substream_t *s1;
-		/* unlock all streams */
+	}
+	snd_pcm_group_for_each(pos, substream) {
+		s = snd_pcm_group_substream_entry(pos);
+		ops->post_action(s, state);
+	}
+ _unlock:
+	if (do_lock) {
+		/* unlock streams */
 		snd_pcm_group_for_each(pos, substream) {
 			s1 = snd_pcm_group_substream_entry(pos);
 			if (s1 != substream)
@@ -716,9 +722,10 @@
 	if (res < 0)
 		return res;
 	res = ops->do_action(substream, state);
-	if (res == 0) {
+	if (res == 0)
 		ops->post_action(substream, state);
-	}
+	else if (ops->undo_action)
+		ops->undo_action(substream, state);
 	return res;
 }
 
@@ -787,6 +794,9 @@
 	return res;
 }
 
+/*
+ * start callbacks
+ */
 static int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
@@ -803,14 +813,20 @@
 {
 	if (substream->runtime->trigger_master != substream)
 		return 0;
-        return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+}
+
+static void snd_pcm_undo_start(snd_pcm_substream_t *substream, int state)
+{
+	if (substream->runtime->trigger_master == substream)
+		substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
 }
 
 static void snd_pcm_post_start(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	snd_pcm_trigger_tstamp(substream);
-	runtime->status->state = SNDRV_PCM_STATE_RUNNING;
+	runtime->status->state = state;
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
 	    runtime->silence_size > 0)
 		snd_pcm_playback_silence(substream, ULONG_MAX);
@@ -823,22 +839,27 @@
 static struct action_ops snd_pcm_action_start = {
 	.pre_action = snd_pcm_pre_start,
 	.do_action = snd_pcm_do_start,
+	.undo_action = snd_pcm_undo_start,
 	.post_action = snd_pcm_post_start
 };
 
 /**
  * snd_pcm_start
+ *
+ * Start all linked streams.
  */
 int snd_pcm_start(snd_pcm_substream_t *substream)
 {
-	return snd_pcm_action(&snd_pcm_action_start, substream, 0);
+	return snd_pcm_action(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
 }
 
+/*
+ * stop callbacks
+ */
 static int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
-	if (substream->runtime->status->state != SNDRV_PCM_STATE_RUNNING &&
-	    substream->runtime->status->state != SNDRV_PCM_STATE_DRAINING)
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
 		return -EBADFD;
 	runtime->trigger_master = substream;
 	return 0;
@@ -846,19 +867,22 @@
 
 static int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state)
 {
-	if (substream->runtime->trigger_master != substream)
-		return 0;
-	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+	if (substream->runtime->trigger_master == substream &&
+	    snd_pcm_running(substream))
+		substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+	return 0; /* unconditonally stop all substreams */
 }
 
 static void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
-	snd_pcm_trigger_tstamp(substream);
-	if (substream->timer)
-		snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp);
-	runtime->status->state = state;
-	snd_pcm_tick_set(substream, 0);
+	if (runtime->status->state != state) {
+		snd_pcm_trigger_tstamp(substream);
+		if (substream->timer)
+			snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp);
+		runtime->status->state = state;
+		snd_pcm_tick_set(substream, 0);
+	}
 	wake_up(&runtime->sleep);
 }
 
@@ -870,12 +894,30 @@
 
 /**
  * snd_pcm_stop
+ *
+ * Try to stop all running streams in the substream group.
+ * The state of each stream is changed to the given value after that unconditionally.
  */
 int snd_pcm_stop(snd_pcm_substream_t *substream, int state)
 {
 	return snd_pcm_action(&snd_pcm_action_stop, substream, state);
 }
 
+/**
+ * snd_pcm_drain_done
+ *
+ * Stop the DMA only when the given stream is playback.
+ * The state is changed to SETUP.
+ * Unlike snd_pcm_stop(), this affects only the given stream.
+ */
+int snd_pcm_drain_done(snd_pcm_substream_t *substream)
+{
+	return snd_pcm_action_single(&snd_pcm_action_stop, substream, SNDRV_PCM_STATE_SETUP);
+}
+
+/*
+ * pause callbacks
+ */
 static int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
@@ -899,6 +941,14 @@
 					      SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
 }
 
+static void snd_pcm_undo_pause(snd_pcm_substream_t *substream, int push)
+{
+	if (substream->runtime->trigger_master == substream)
+		substream->ops->trigger(substream,
+					push ? SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
+					SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+}
+
 static void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
@@ -921,9 +971,13 @@
 static struct action_ops snd_pcm_action_pause = {
 	.pre_action = snd_pcm_pre_pause,
 	.do_action = snd_pcm_do_pause,
+	.undo_action = snd_pcm_undo_pause,
 	.post_action = snd_pcm_post_pause
 };
 
+/*
+ * Push/release the pause for all linked streams.
+ */
 static int snd_pcm_pause(snd_pcm_substream_t *substream, int push)
 {
 	return snd_pcm_action(&snd_pcm_action_pause, substream, push);
@@ -937,7 +991,6 @@
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
 		return -EBUSY;
-	runtime->status->suspended_state = runtime->status->state;
 	runtime->trigger_master = substream;
 	return 0;
 }
@@ -947,10 +1000,10 @@
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	if (runtime->trigger_master != substream)
 		return 0;
-	if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING &&
-	    runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING)
+	if (! snd_pcm_running(substream))
 		return 0;
-	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+	substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+	return 0; /* suspend unconditionally */
 }
 
 static void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state)
@@ -959,6 +1012,7 @@
 	snd_pcm_trigger_tstamp(substream);
 	if (substream->timer)
 		snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp);
+	runtime->status->suspended_state = runtime->status->state;
 	runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
 	snd_pcm_tick_set(substream, 0);
 	wake_up(&runtime->sleep);
@@ -972,6 +1026,9 @@
 
 /**
  * snd_pcm_suspend
+ *
+ * Trigger SUSPEND to all linked streams.
+ * After this call, all streams are changed to SUSPENDED state.
  */
 int snd_pcm_suspend(snd_pcm_substream_t *substream)
 {
@@ -980,11 +1037,14 @@
 
 /**
  * snd_pcm_suspend_all
+ *
+ * Trigger SUSPEND to all substreams in the given pcm.
+ * After this call, all streams are changed to SUSPENDED state.
  */
 int snd_pcm_suspend_all(snd_pcm_t *pcm)
 {
 	snd_pcm_substream_t *substream;
-	int stream, err;
+	int stream, err = 0;
 
 	for (stream = 0; stream < 2; stream++) {
 		for (substream = pcm->streams[stream].substream; substream; substream = substream->next) {
@@ -992,15 +1052,11 @@
 			if (substream->runtime == NULL)
 				continue;
 			snd_pcm_stream_lock(substream);
-			if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
-				snd_pcm_stream_unlock(substream);
-				continue;
-			}
-			if ((err = snd_pcm_suspend(substream)) < 0) {
-				snd_pcm_stream_unlock(substream);
-				return err;
-			}
+			if (substream->runtime->status->state != SNDRV_PCM_STATE_SUSPENDED)
+				err = snd_pcm_suspend(substream);
 			snd_pcm_stream_unlock(substream);
+			if (err < 0)
+				return err;
 		}
 	}
 	return 0;
@@ -1022,12 +1078,21 @@
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	if (runtime->trigger_master != substream)
 		return 0;
+	/* DMA not running previously? */
 	if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING &&
-	    runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING)
+	    (runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING ||
+	     substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
 		return 0;
 	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);
 }
 
+static void snd_pcm_undo_resume(snd_pcm_substream_t *substream, int state)
+{
+	if (substream->runtime->trigger_master == substream &&
+	    snd_pcm_running(substream))
+		substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+}
+
 static void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
@@ -1042,6 +1107,7 @@
 static struct action_ops snd_pcm_action_resume = {
 	.pre_action = snd_pcm_pre_resume,
 	.do_action = snd_pcm_do_resume,
+	.undo_action = snd_pcm_undo_resume,
 	.post_action = snd_pcm_post_resume
 };
 
@@ -1066,6 +1132,11 @@
 
 #endif /* CONFIG_PM */
 
+/*
+ * xrun ioctl
+ *
+ * Change the RUNNING stream(s) to XRUN state.
+ */
 static int snd_pcm_xrun(snd_pcm_substream_t *substream)
 {
 	snd_card_t *card = substream->pcm->card;
@@ -1073,8 +1144,13 @@
 	int result;
 
 	snd_power_lock(card);
+	if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+		if (result < 0)
+			goto _unlock;
+	}
+
 	snd_pcm_stream_lock_irq(substream);
-       _xrun_recovery:
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_XRUN:
 		result = 0;	/* already there */
@@ -1082,21 +1158,18 @@
 	case SNDRV_PCM_STATE_RUNNING:
 		result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
 		break;
-	case SNDRV_PCM_STATE_SUSPENDED:
-		snd_pcm_stream_unlock_irq(substream);
-		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
-		snd_pcm_stream_lock_irq(substream);
-		if (result >= 0)
-			goto _xrun_recovery;
-		break;
 	default:
 		result = -EBADFD;
 	}
 	snd_pcm_stream_unlock_irq(substream);
+ _unlock:
 	snd_power_unlock(card);
 	return result;
 }
 
+/*
+ * reset ioctl
+ */
 static int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
@@ -1145,17 +1218,17 @@
 	return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0);
 }
 
+/*
+ * prepare ioctl
+ */
 static int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
-	switch (runtime->status->state) {
-	case SNDRV_PCM_STATE_OPEN:
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
 		return -EBADFD;
-	case SNDRV_PCM_STATE_RUNNING:
+	if (snd_pcm_running(substream))
 		return -EBUSY;
-	default:
-		return 0;
-	}
+	return 0;
 }
 
 static int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state)
@@ -1195,111 +1268,147 @@
 	return res;
 }
 
-static void snd_pcm_change_state(snd_pcm_substream_t *substream, int state)
+/*
+ * drain ioctl
+ */
+
+static int snd_pcm_pre_drain_init(snd_pcm_substream_t * substream, int state)
 {
-	struct list_head *pos;
-	snd_pcm_substream_t *s;
+	if (substream->ffile->f_flags & O_NONBLOCK)
+		return -EAGAIN;
+	substream->runtime->trigger_master = substream;
+	return 0;
+}
 
-	if (snd_pcm_stream_linked(substream)) {
-		if (!spin_trylock(&substream->group->lock)) {
-			spin_unlock(&substream->self_group.lock);
-			spin_lock(&substream->group->lock);
-			spin_lock(&substream->self_group.lock);
-		}
-		snd_pcm_group_for_each(pos, substream) {
-			s = snd_pcm_group_substream_entry(pos);
-			if (s != substream)
-				spin_lock(&s->self_group.lock);
-			s->runtime->status->state = state;
-			if (s != substream)
-				spin_unlock(&s->self_group.lock);
+static int snd_pcm_do_drain_init(snd_pcm_substream_t * substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		switch (runtime->status->state) {
+		case SNDRV_PCM_STATE_PREPARED:
+			/* start playback stream if possible */
+			if (! snd_pcm_playback_empty(substream)) {
+				snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING);
+				snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING);
+			}
+			break;
+		case SNDRV_PCM_STATE_RUNNING:
+			runtime->status->state = SNDRV_PCM_STATE_DRAINING;
+			break;
+		default:
+			break;
 		}
-		spin_unlock(&substream->group->lock);
 	} else {
-		substream->runtime->status->state = state;
+		/* stop running stream */
+		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
+			int state = snd_pcm_capture_avail(runtime) > 0 ?
+				SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP;
+			snd_pcm_do_stop(substream, state);
+			snd_pcm_post_stop(substream, state);
+		}
 	}
+	return 0;
+}
+
+static void snd_pcm_post_drain_init(snd_pcm_substream_t * substream, int state)
+{
 }
 
-static int snd_pcm_playback_drop(snd_pcm_substream_t *substream);
+static struct action_ops snd_pcm_action_drain_init = {
+	.pre_action = snd_pcm_pre_drain_init,
+	.do_action = snd_pcm_do_drain_init,
+	.post_action = snd_pcm_post_drain_init
+};
 
-static int snd_pcm_playback_drain(snd_pcm_substream_t * substream)
+struct drain_rec {
+	snd_pcm_substream_t *substream;
+	wait_queue_t wait;
+	snd_pcm_uframes_t stop_threshold;
+};
+
+static int snd_pcm_drop(snd_pcm_substream_t *substream);
+
+/*
+ * Drain the stream(s).
+ * When the substream is linked, sync until the draining of all playback streams
+ * is finished.
+ * After this call, all streams are supposed to be either SETUP or DRAINING
+ * (capture only) state.
+ */
+static int snd_pcm_drain(snd_pcm_substream_t *substream)
 {
 	snd_card_t *card;
 	snd_pcm_runtime_t *runtime;
-	int err, result = 0;
-	wait_queue_t wait;
-	enum { READY, EXPIRED, SUSPENDED, SIGNALED } state = READY;
-	snd_pcm_uframes_t stop_threshold;
+	struct list_head *pos;
+	int result = 0;
+	int i, num_drecs;
+	struct drain_rec *drec, drec_tmp, *d;
 
 	snd_assert(substream != NULL, return -ENXIO);
-	snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);
-	runtime = substream->runtime;
 	card = substream->pcm->card;
+	runtime = substream->runtime;
 
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+
+	down_read(&snd_pcm_link_rwsem);
 	snd_power_lock(card);
-	snd_pcm_stream_lock_irq(substream);
+	if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+		if (result < 0)
+			goto _unlock;
+	}
 
-	/* stop_threshold fixup to avoid endless loop when */
-	/* stop_threshold > buffer_size */
-	stop_threshold = runtime->stop_threshold;
-	if (runtime->stop_threshold > runtime->buffer_size)
-		runtime->stop_threshold = runtime->buffer_size;
+	/* allocate temporary record for drain sync */
+	if (snd_pcm_stream_linked(substream)) {
+		drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
+		if (! drec) {
+			result = -ENOMEM;
+			goto _unlock;
+		}
+	} else
+		drec = &drec_tmp;
 
-	switch (runtime->status->state) {
-	case SNDRV_PCM_STATE_PAUSED:
+	snd_pcm_stream_lock_irq(substream);
+	/* resume pause */
+	if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
 		snd_pcm_pause(substream, 0);
-		/* Fall through */
-	case SNDRV_PCM_STATE_RUNNING:
-	case SNDRV_PCM_STATE_DRAINING:
-		break;
-	case SNDRV_PCM_STATE_SUSPENDED:
-		snd_pcm_stream_unlock_irq(substream);
-		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
-		snd_pcm_stream_lock_irq(substream);
-		if (result >= 0)
-			snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
-		goto _end;
-	case SNDRV_PCM_STATE_OPEN:
-		result = -EBADFD;
-		goto _end;
-	case SNDRV_PCM_STATE_PREPARED:
-		if (!snd_pcm_playback_empty(substream)) {
-			err = snd_pcm_start(substream);
-			if (err < 0) {
-				result = err;
-				goto _end;
-			}
-			break;
-		}
-		/* Fall through */
-	case SNDRV_PCM_STATE_XRUN:
-		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
-		/* Fall through */
-	case SNDRV_PCM_STATE_SETUP:
+
+	/* pre-start/stop - all running streams are changed to DRAINING state */
+	result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
+	if (result < 0)
 		goto _end;
-	default: 
-		break; 
-	}
 
-	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
-		if (snd_pcm_playback_empty(substream)) {
-			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
-			goto _end;
+	/* check streams with PLAYBACK & DRAINING */
+	num_drecs = 0;
+	snd_pcm_group_for_each(pos, substream) {
+		snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos);
+		runtime = s->runtime;
+		if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) {
+			runtime->status->state = SNDRV_PCM_STATE_SETUP;
+			continue;
+		}
+		if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			d = &drec[num_drecs++];
+			d->substream = s;
+			init_waitqueue_entry(&d->wait, current);
+			add_wait_queue(&runtime->sleep, &d->wait);
+			/* stop_threshold fixup to avoid endless loop when
+			 * stop_threshold > buffer_size
+			 */
+			d->stop_threshold = runtime->stop_threshold;
+			if (runtime->stop_threshold > runtime->buffer_size)
+				runtime->stop_threshold = runtime->buffer_size;
 		}
-		snd_pcm_change_state(substream, SNDRV_PCM_STATE_DRAINING);
 	}
 
-	if (substream->ffile->f_flags & O_NONBLOCK) {
-		result = -EAGAIN;
+	if (! num_drecs)
 		goto _end;
-	}
 
-	init_waitqueue_entry(&wait, current);
-	add_wait_queue(&runtime->sleep, &wait);
-	while (1) {
+	for (;;) {
 		long tout;
 		if (signal_pending(current)) {
-			state = SIGNALED;
+			result = -ERESTARTSYS;
 			break;
 		}
 		set_current_state(TASK_INTERRUPTIBLE);
@@ -1309,177 +1418,81 @@
 		snd_power_lock(card);
 		snd_pcm_stream_lock_irq(substream);
 		if (tout == 0) {
-			state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
+			if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+				result = -ESTRPIPE;
+			else {
+				snd_printd("playback drain error (DMA or IRQ trouble?)\n");
+				snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+				result = -EIO;
+			}
 			break;
 		}
-		if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) {
-			state = READY;
-			break;
+		/* all finished? */
+		for (i = 0; i < num_drecs; i++) {
+			runtime = drec[i].substream->runtime;
+			if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+				break;
 		}
+		if (i == num_drecs)
+			break;
 	}
-	remove_wait_queue(&runtime->sleep, &wait);
-
-	switch (state) {
-	case SIGNALED:
-		result = -ERESTARTSYS;
-		goto _end;
-	case SUSPENDED:
-		result = -ESTRPIPE;
-		goto _end;
-	case EXPIRED:
-		snd_printd("playback drain error (DMA or IRQ trouble?)\n");
-		result = -EIO;
-		goto _end;
-	default:
-		break;
+	for (i = 0; i < num_drecs; i++) {
+		d = &drec[i];
+		runtime = d->substream->runtime;
+		remove_wait_queue(&runtime->sleep, &d->wait);
+		runtime->stop_threshold = d->stop_threshold;
 	}
 
-      _end:
-	runtime->stop_threshold = stop_threshold;
+ _end:
 	snd_pcm_stream_unlock_irq(substream);
+	if (drec && drec != &drec_tmp)
+		kfree(drec);
+ _unlock:
 	snd_power_unlock(card);
-	if (state == EXPIRED)
-		snd_pcm_playback_drop(substream);
+	up_read(&snd_pcm_link_rwsem);
 
 	return result;
 }
 
-static int snd_pcm_playback_drop(snd_pcm_substream_t *substream)
+/*
+ * drop ioctl
+ *
+ * Immediately put all linked substreams into SETUP state.
+ */
+static int snd_pcm_drop(snd_pcm_substream_t *substream)
 {
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	snd_card_t *card = substream->pcm->card;
-	int res = 0;
+	snd_pcm_runtime_t *runtime;
+	snd_card_t *card;
+	int result = 0;
 	
-	snd_power_lock(card);
-	snd_pcm_stream_lock_irq(substream);
-	switch (runtime->status->state) {
-	case SNDRV_PCM_STATE_OPEN:
-		res = -EBADFD;
-		break;
-	case SNDRV_PCM_STATE_SETUP:
-		break;
-	case SNDRV_PCM_STATE_PAUSED:
-		snd_pcm_pause(substream, 0);
-		/* Fall through */
-	case SNDRV_PCM_STATE_RUNNING:
-	case SNDRV_PCM_STATE_DRAINING:
-		if (snd_pcm_update_hw_ptr(substream) >= 0) {
-			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
-			break;
-		}
-		/* Fall through */
-	case SNDRV_PCM_STATE_PREPARED:
-	case SNDRV_PCM_STATE_XRUN:
-		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
-		break;
-	case SNDRV_PCM_STATE_SUSPENDED:
-		snd_pcm_stream_unlock_irq(substream);
-		res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
-		snd_pcm_stream_lock_irq(substream);
-		if (res >= 0)
-			snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
-		break;
-	default:
-		break; 
-	}
-	runtime->control->appl_ptr = runtime->status->hw_ptr;
-	snd_pcm_stream_unlock_irq(substream);
-	snd_power_unlock(card);
-	return res;
-}
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	card = substream->pcm->card;
 
-static int snd_pcm_capture_drain(snd_pcm_substream_t * substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	snd_card_t *card = substream->pcm->card;
-	int res = 0;
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
 
 	snd_power_lock(card);
-	snd_pcm_stream_lock_irq(substream);
-	switch (runtime->status->state) {
-	case SNDRV_PCM_STATE_OPEN:
-		res = -EBADFD;
-		break;
-	case SNDRV_PCM_STATE_PREPARED:
-		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
-		break;
-	case SNDRV_PCM_STATE_SETUP:
-	case SNDRV_PCM_STATE_DRAINING:
-		break;
-	case SNDRV_PCM_STATE_PAUSED:
-		snd_pcm_pause(substream, 0);
-		/* Fall through */
-	case SNDRV_PCM_STATE_RUNNING:
-		if (snd_pcm_update_hw_ptr(substream) >= 0) {
-			snd_pcm_stop(substream, 
-				     snd_pcm_capture_avail(runtime) > 0 ?
-				     SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP);
-			break;
-		}
-		/* Fall through */
-	case SNDRV_PCM_STATE_XRUN:
-	       _xrun_recovery:
-		snd_pcm_change_state(substream, 
-				     snd_pcm_capture_avail(runtime) > 0 ?
-				     SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP);
-		break;
-	case SNDRV_PCM_STATE_SUSPENDED:
-		snd_pcm_stream_unlock_irq(substream);
-		res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
-		snd_pcm_stream_lock_irq(substream);
-		if (res >= 0)
-			goto _xrun_recovery;
-		break;
-	default: 
-		break; 
+	if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+		if (result < 0)
+			goto _unlock;
 	}
-	snd_pcm_stream_unlock_irq(substream);
-	snd_power_unlock(card);
-	return res;
-}
 
-static int snd_pcm_capture_drop(snd_pcm_substream_t * substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	snd_card_t *card = substream->pcm->card;
-	int res = 0;
-
-	snd_power_lock(card);
 	snd_pcm_stream_lock_irq(substream);
-	switch (runtime->status->state) {
-	case SNDRV_PCM_STATE_OPEN:
-		res = -EBADFD;
-		break;
-	case SNDRV_PCM_STATE_SETUP:
-		break;
-	case SNDRV_PCM_STATE_PAUSED:
+	/* resume pause */
+	if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
 		snd_pcm_pause(substream, 0);
-		/* Fall through */
-	case SNDRV_PCM_STATE_RUNNING:
-		snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
-		break;
-	case SNDRV_PCM_STATE_SUSPENDED:
-		snd_pcm_stream_unlock_irq(substream);
-		res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
-		snd_pcm_stream_lock_irq(substream);
-		if (res < 0)
-			goto _end;
-		/* Fall through */
-	case SNDRV_PCM_STATE_PREPARED:
-	case SNDRV_PCM_STATE_DRAINING:
-	case SNDRV_PCM_STATE_XRUN:
-		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
-		break;
-	default: 
-		break; 
-	}
-	runtime->control->appl_ptr = runtime->status->hw_ptr;
-       _end:
+
+	snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+	/* runtime->control->appl_ptr = runtime->status->hw_ptr; */
 	snd_pcm_stream_unlock_irq(substream);
+ _unlock:
 	snd_power_unlock(card);
-	return res;
+	return result;
 }
 
+
 /* WARNING: Don't forget to fput back the file */
 extern int snd_major;
 static struct file *snd_pcm_file_fd(int fd)
@@ -1505,6 +1518,9 @@
 	return file;
 }
 
+/*
+ * PCM link handling
+ */
 static int snd_pcm_link(snd_pcm_substream_t *substream, int fd)
 {
 	int res = 0;
@@ -1512,12 +1528,6 @@
 	snd_pcm_file_t *pcm_file;
 	snd_pcm_substream_t *substream1;
 
-	snd_pcm_stream_lock_irq(substream);
-	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-		snd_pcm_stream_unlock_irq(substream);
-		return -EBADFD;
-	}
-	snd_pcm_stream_unlock_irq(substream);
 	file = snd_pcm_file_fd(fd);
 	if (!file)
 		return -EBADFD;
@@ -1525,7 +1535,8 @@
 	substream1 = pcm_file->substream;
 	down_write(&snd_pcm_link_rwsem);
 	write_lock_irq(&snd_pcm_link_rwlock);
-	if (substream->runtime->status->state != substream1->runtime->status->state) {
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+	    substream->runtime->status->state != substream1->runtime->status->state) {
 		res = -EBADFD;
 		goto _end;
 	}
@@ -1542,8 +1553,10 @@
 		spin_lock_init(&substream->group->lock);
 		INIT_LIST_HEAD(&substream->group->substreams);
 		list_add_tail(&substream->link_list, &substream->group->substreams);
+		substream->group->count = 1;
 	}
 	list_add_tail(&substream1->link_list, &substream->group->substreams);
+	substream->group->count++;
 	substream1->group = substream->group;
  _end:
 	write_unlock_irq(&snd_pcm_link_rwlock);
@@ -1562,7 +1575,7 @@
 static int snd_pcm_unlink(snd_pcm_substream_t *substream)
 {
 	struct list_head *pos;
-	int res = 0, count = 0;
+	int res = 0;
 
 	down_write(&snd_pcm_link_rwsem);
 	write_lock_irq(&snd_pcm_link_rwlock);
@@ -1571,11 +1584,8 @@
 		goto _end;
 	}
 	list_del(&substream->link_list);
-	snd_pcm_group_for_each(pos, substream) {
-		if (++count > 1)
-			break;
-	}
-	if (count == 1) {	/* detach the last stream, too */
+	substream->group->count--;
+	if (substream->group->count == 1) {	/* detach the last stream, too */
 		snd_pcm_group_for_each(pos, substream) {
 			relink_to_local(snd_pcm_group_substream_entry(pos));
 			break;
@@ -1589,6 +1599,9 @@
 	return res;
 }
 
+/*
+ * hw configurator
+ */
 static int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params,
 			       snd_pcm_hw_rule_t *rule)
 {
@@ -2077,10 +2090,7 @@
 	snd_assert(substream != NULL, return -ENXIO);
 	snd_assert(!atomic_read(&substream->runtime->mmap_count), );
 	pcm = substream->pcm;
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		snd_pcm_playback_drop(substream);
-	else
-		snd_pcm_capture_drop(substream);
+	snd_pcm_drop(substream);
 	fasync_helper(-1, file, 0, &substream->runtime->fasync);
 	down(&pcm->open_mutex);
 	snd_pcm_release_file(pcm_file);
@@ -2436,7 +2446,7 @@
 	case SNDRV_PCM_IOCTL_RESET:
 		return snd_pcm_reset(substream);
 	case SNDRV_PCM_IOCTL_START:
-		return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, 0);
+		return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
 	case SNDRV_PCM_IOCTL_LINK:
 		return snd_pcm_link(substream, (int)(unsigned long) arg);
 	case SNDRV_PCM_IOCTL_UNLINK:
@@ -2455,6 +2465,10 @@
 		return snd_pcm_hw_refine_old_user(substream, arg);
 	case SNDRV_PCM_IOCTL_HW_PARAMS_OLD:
 		return snd_pcm_hw_params_old_user(substream, arg);
+	case SNDRV_PCM_IOCTL_DRAIN:
+		return snd_pcm_drain(substream);
+	case SNDRV_PCM_IOCTL_DROP:
+		return snd_pcm_drop(substream);
 	}
 	snd_printd("unknown ioctl = 0x%x\n", cmd);
 	return -ENOTTY;
@@ -2543,10 +2557,6 @@
 		snd_pcm_stream_unlock_irq(substream);
 		return res;
 	}
-	case SNDRV_PCM_IOCTL_DRAIN:
-		return snd_pcm_playback_drain(substream);
-	case SNDRV_PCM_IOCTL_DROP:
-		return snd_pcm_playback_drop(substream);
 	}
 	return snd_pcm_common_ioctl1(substream, cmd, arg);
 }
@@ -2626,10 +2636,6 @@
 		__put_user(result, _frames);
 		return result < 0 ? result : 0;
 	}
-	case SNDRV_PCM_IOCTL_DRAIN:
-		return snd_pcm_capture_drain(substream);
-	case SNDRV_PCM_IOCTL_DROP:
-		return snd_pcm_capture_drop(substream);
 	}
 	return snd_pcm_common_ioctl1(substream, cmd, arg);
 }
Index: alsa-kernel/include/pcm.h
===================================================================
RCS file: /home/tiwai/cvs/alsa/alsa-kernel/include/pcm.h,v
retrieving revision 1.49
diff -u -r1.49 pcm.h
--- alsa-kernel/include/pcm.h	15 Sep 2004 09:04:26 -0000	1.49
+++ alsa-kernel/include/pcm.h	15 Sep 2004 23:04:59 -0000
@@ -364,6 +364,7 @@
 typedef struct _snd_pcm_group {		/* keep linked substreams */
 	spinlock_t lock;
 	struct list_head substreams;
+	int count;
 } snd_pcm_group_t;
 
 struct _snd_pcm_substream {
@@ -488,6 +489,7 @@
 int snd_pcm_prepare(snd_pcm_substream_t *substream);
 int snd_pcm_start(snd_pcm_substream_t *substream);
 int snd_pcm_stop(snd_pcm_substream_t *substream, int status);
+int snd_pcm_drain_done(snd_pcm_substream_t *substream);
 #ifdef CONFIG_PM
 int snd_pcm_suspend(snd_pcm_substream_t *substream);
 int snd_pcm_suspend_all(snd_pcm_t *pcm);

  reply	other threads:[~2004-09-16 11:16 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-09-05 18:51 Kernel crash Giuliano Pochini
2004-09-06 15:16 ` Takashi Iwai
2004-09-07  7:55   ` Giuliano Pochini
2004-09-11 19:02     ` [PATCH] " Giuliano Pochini
2004-09-13 15:00       ` Takashi Iwai
2004-09-13 20:07         ` Giuliano Pochini
2004-09-15 10:24           ` Takashi Iwai
2004-09-15 17:50             ` Giuliano Pochini
2004-09-15 18:01               ` Takashi Iwai
2004-09-16 11:16                 ` Takashi Iwai [this message]
2004-09-16 19:56                   ` Giuliano Pochini
2004-09-17  6:54               ` Jaroslav Kysela
2004-09-17 10:22                 ` Takashi Iwai
2004-09-17 10:32                   ` Takashi Iwai
2004-09-22  8:08                     ` Jaroslav Kysela
2004-09-22 10:08                       ` Takashi Iwai
2004-09-22 14:12                         ` Giuliano Pochini
2004-09-23  7:43                         ` Jaroslav Kysela
2004-09-24 13:30                           ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=s5hacvqsbty.wl@alsa2.suse.de \
    --to=tiwai@suse.de \
    --cc=alsa-devel@lists.sourceforge.net \
    --cc=pochini@shiny.it \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox