[alsa-devel] [PATCH 1/3] ALSA: add snd_card_disconnect_sync()

Kuninori Morimoto kuninori.morimoto.gx at renesas.com
Wed Oct 11 08:36:13 CEST 2017


From: Takashi Iwai <tiwai at suse.de>

In case of user unbind ALSA driver during playbacking/capturing,
each driver needs to stop and remove it correctly. One note here is
that we can't cancel from remove function in such case, becasue
unbind operation doesn't check return value from remove function.
So, we *must* stop and remove in this case.

For this purpose, we need to sync (= wait) until the all top-level
operations are canceled at remove function.
For example, snd_card_free() processes the isconnection procedure at
first, then waits for the completion. That's how the hot-unplug works
safely. It's implemented, at least, in the top-level driver removal.

Now for the lower level driver, we need a similar strategy. Notify to
the toplevel for hot-unplug (disconnect in ALSA), and sync with the
stop operation, then continue the rest of its own remove procedure.

This patch adds snd_card_disconnect_sync(), and driver can use it from
remove function.

Signed-off-by: Takashi Iwai <tiwai at suse.de>
Tested-by: Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
---
Takashi-san

I created this patch and its git-log, but please fixup it
if needed.

 include/sound/core.h |  2 ++
 sound/core/init.c    | 18 ++++++++++++++++++
 sound/core/pcm.c     |  4 ++++
 3 files changed, 24 insertions(+)

diff --git a/include/sound/core.h b/include/sound/core.h
index 4104a9d..5f181b8 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -133,6 +133,7 @@ struct snd_card {
 	struct device card_dev;		/* cardX object for sysfs */
 	const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
 	bool registered;		/* card_dev is registered? */
+	wait_queue_head_t remove_sleep;
 
 #ifdef CONFIG_PM
 	unsigned int power_state;	/* power state */
@@ -240,6 +241,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
 		 struct snd_card **card_ret);
 
 int snd_card_disconnect(struct snd_card *card);
+void snd_card_disconnect_sync(struct snd_card *card);
 int snd_card_free(struct snd_card *card);
 int snd_card_free_when_closed(struct snd_card *card);
 void snd_card_set_id(struct snd_card *card, const char *id);
diff --git a/sound/core/init.c b/sound/core/init.c
index 32ebe2f..f7f7050 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -255,6 +255,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
 #ifdef CONFIG_PM
 	init_waitqueue_head(&card->power_sleep);
 #endif
+	init_waitqueue_head(&card->remove_sleep);
 
 	device_initialize(&card->card_dev);
 	card->card_dev.parent = parent;
@@ -452,6 +453,21 @@ int snd_card_disconnect(struct snd_card *card)
 }
 EXPORT_SYMBOL(snd_card_disconnect);
 
+void snd_card_disconnect_sync(struct snd_card *card)
+{
+	DECLARE_COMPLETION(comp);
+
+	if (snd_card_disconnect(card) < 0)
+		return;
+
+	spin_lock_irq(&card->files_lock);
+	wait_event_lock_irq(card->remove_sleep,
+			    list_empty(&card->files_list),
+			    card->files_lock);
+	spin_unlock_irq(&card->files_lock);
+}
+EXPORT_SYMBOL_GPL(snd_card_disconnect_sync);
+
 static int snd_card_do_free(struct snd_card *card)
 {
 #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
@@ -957,6 +973,8 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
 			break;
 		}
 	}
+	if (list_empty(&card->files_list))
+		wake_up_all(&card->remove_sleep);
 	spin_unlock(&card->files_lock);
 	if (!found) {
 		dev_err(card->dev, "card file remove problem (%p)\n", file);
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 7eadb7f..054e47a 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1152,6 +1152,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
 		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) {
 			snd_pcm_stream_lock_irq(substream);
 			if (substream->runtime) {
+				if (snd_pcm_running(substream))
+					snd_pcm_stop(substream,
+						     SNDRV_PCM_STATE_DISCONNECTED);
+				/* to be sure, set the state unconditionally */
 				substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
 				wake_up(&substream->runtime->sleep);
 				wake_up(&substream->runtime->tsleep);
-- 
1.9.1



More information about the Alsa-devel mailing list