summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-04-03 17:41:32 +1100
committerMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-04-03 17:41:32 +1100
commitde95b5d9cb985acf9e28ea4e1a8592d335e601b1 (patch)
treeaf02bdc00e2c2dd0242e97fe1dc8002ca6df24ae
parente152900bc73f1f7d3b33aa5369c36da762b0b042 (diff)
Introduce 'unconfigure' callback
The 'unconfigure' callback can be used to clean up the 'configured' state of an Audio Path, for when it is desired to reset the streams, and/or reload a different stream configuration. To destroy the streams, we must attempt to force their PCMs closed. Take note though, that if a PCM is being written to when an unconfigure command is issued, system instability may occur. Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
-rw-r--r--configfs.c9
-rw-r--r--core.c51
-rw-r--r--core.h4
-rw-r--r--loopback/loopback.c11
-rw-r--r--sound/avirt.h3
5 files changed, 73 insertions, 5 deletions
diff --git a/configfs.c b/configfs.c
index 4f88bf8..8f2d452 100644
--- a/configfs.c
+++ b/configfs.c
@@ -285,8 +285,7 @@ static void cfg_snd_avirt_stream_release(struct config_item *item)
}
D_INFOK("Release stream: %s", stream->name);
- kfree(stream->pcm_ops);
- kfree(stream);
+ snd_avirt_stream_try_destroy(stream);
}
static void cfg_snd_avirt_route_release(struct config_item *item)
@@ -420,12 +419,12 @@ cfg_snd_avirt_stream_group_configured_store(struct config_item *item,
CHK_ERR(kstrtoul(p, 10, &tmp));
- if (tmp != 1) {
- D_ERRORK("streams can only be sealed, not unsealed!");
+ if (tmp > 1) {
+ D_ERRORK("Configure streams must be 0 or 1!");
return -ERANGE;
}
- snd_avirt_streams_seal();
+ (tmp) ? snd_avirt_streams_configure() : snd_avirt_streams_unconfigure();
return count;
}
diff --git a/core.c b/core.c
index d9e264c..c2e32c5 100644
--- a/core.c
+++ b/core.c
@@ -112,6 +112,30 @@ static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream)
return pcm;
}
+void snd_avirt_stream_try_destroy(struct snd_avirt_stream *stream)
+{
+ unsigned long _flags;
+ struct snd_pcm_substream *substream =
+ stream->pcm->streams[stream->direction].substream;
+
+ snd_pcm_stream_lock_irqsave(substream, _flags);
+ if (substream->runtime) {
+ if (snd_pcm_running(substream)) {
+ if (0 !=
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SUSPENDED))
+ D_ERRORK("Could not stop PCM '%s'",
+ stream->name);
+ }
+ }
+ snd_pcm_stream_unlock_irqrestore(substream, _flags);
+
+ snd_device_free(core.card, stream->pcm);
+ kfree(stream->pcm_ops);
+ kfree(stream);
+
+ core.stream_count--;
+}
+
static struct snd_avirt_route *snd_avirt_route_get(const char *uid)
{
struct list_head *entry;
@@ -620,6 +644,33 @@ int snd_avirt_streams_configure(void)
return err;
}
+int snd_avirt_streams_unconfigure(void)
+{
+ int i = 0, err = 0;
+ struct snd_avirt_audiopath_obj *ap_obj;
+ struct snd_avirt_stream_array stream_array;
+
+ if (!core.streams_configured) {
+ D_ERRORK("streams are already unconfigured!");
+ return -1;
+ }
+
+ list_for_each_entry (ap_obj, &audiopath_list, list) {
+ if (!ap_obj->path->unconfigure) {
+ D_ERRORK("Cannot do 'unconfigure' for AP: %s",
+ ap_obj->path->uid);
+ return -EFAULT;
+ }
+
+ D_INFOK("Do 'unconfigure' for AP: %s", ap_obj->path->uid);
+ ap_obj->path->unconfigure();
+ }
+
+ core.streams_configured = false;
+
+ return 0;
+}
+
bool snd_avirt_streams_configured(void)
{
return core.streams_configured;
diff --git a/core.h b/core.h
index 6f63e8c..c39002a 100644
--- a/core.h
+++ b/core.h
@@ -81,6 +81,8 @@ void snd_avirt_audiopath_destroy_obj(struct snd_avirt_audiopath_obj *p);
*/
int snd_avirt_streams_configure(void);
+int snd_avirt_streams_unconfigure(void);
+
/**
* snd_avirt_streams_configured - Check if streams have been configured or not
* @return: true if configured, false otherwise
@@ -122,6 +124,8 @@ int snd_avirt_route_try_complete(struct snd_avirt_route *route);
* @return: 0 on success, negative ERRNO on failure
*/
int snd_avirt_stream_try_complete(struct snd_avirt_stream *stream);
+void snd_avirt_stream_try_destroy(struct snd_avirt_stream *stream);
+
/**
* snd_avirt_stream_set_map - Set Audio Path mapping for a given stream
* @stream: The stream to assign the mapping to.
diff --git a/loopback/loopback.c b/loopback/loopback.c
index de0f38a..49c188d 100644
--- a/loopback/loopback.c
+++ b/loopback/loopback.c
@@ -1080,6 +1080,14 @@ static int loopbackap_configure(struct snd_card *card,
return 0;
}
+static int loopbackap_unconfigure(void)
+{
+ mutex_destroy(&loopback->cable_lock);
+ kfree(loopback);
+
+ return 0;
+}
+
/*******************************************************************************
* Loopback Audio Path AVIRT registration
******************************************************************************/
@@ -1091,6 +1099,7 @@ static struct snd_avirt_audiopath loopbackap_module = {
.pcm_playback_ops = &loopbackap_pcm_ops,
.pcm_capture_ops = &loopbackap_pcm_ops,
.configure = loopbackap_configure,
+ .unconfigure = loopbackap_unconfigure,
};
static int __init alsa_card_loopback_init(void)
@@ -1108,6 +1117,8 @@ static int __init alsa_card_loopback_init(void)
static void __exit alsa_card_loopback_exit(void)
{
+ mutex_destroy(&loopback->cable_lock);
+ kfree(loopback);
snd_avirt_audiopath_deregister(&loopbackap_module);
}
diff --git a/sound/avirt.h b/sound/avirt.h
index 9259d83..46756b3 100644
--- a/sound/avirt.h
+++ b/sound/avirt.h
@@ -36,6 +36,8 @@ struct snd_avirt_stream_array; /* Forward declaration */
typedef int (*snd_avirt_audiopath_configure)(
struct snd_card *card, struct snd_avirt_stream_array *stream_array);
+typedef int (*snd_avirt_audiopath_unconfigure)(void);
+
typedef void (*snd_avirt_pcm_exttrigger)(void);
/**
@@ -79,6 +81,7 @@ struct snd_avirt_audiopath {
const struct snd_pcm_ops *pcm_playback_ops; /* ALSA PCM playback ops */
const struct snd_pcm_ops *pcm_capture_ops; /* ALSA PCM capture ops */
snd_avirt_audiopath_configure configure; /* Config callback function */
+ snd_avirt_audiopath_unconfigure unconfigure; /* Unconfig cb function */
snd_avirt_pcm_exttrigger pcm_exttrigger; /* External trigger callback */
void *context;
};