diff options
author | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-04-03 17:41:32 +1100 |
---|---|---|
committer | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-04-08 13:57:54 +1000 |
commit | 8e151019a32b733420e2eb3006a5edaac5bc9589 (patch) | |
tree | 0117c22646a7d880b71b1908957a00a347fdf020 | |
parent | 16912fcce2802752ba11b884d920c71cae5eb1c9 (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.c | 9 | ||||
-rw-r--r-- | core.c | 51 | ||||
-rw-r--r-- | core.h | 4 | ||||
-rw-r--r-- | loopback/loopback.c | 11 | ||||
-rw-r--r-- | sound/avirt.h | 3 |
5 files changed, 73 insertions, 5 deletions
@@ -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; } @@ -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; @@ -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; }; |