From 92c383e87fffd5fd22c553e30b9fc94f2cb5f9c0 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 1 Apr 2019 11:58:08 +1100 Subject: Add 'internal' streams for routing, revamp stream finalisation Uses the ALSA internal PCM API to create streams purely used for internal routing purposes. Adds the 'try_complete' functions for both streams and routes in an attempt to standardize the finalization of a stream or route object. Signed-off-by: Mark Farrugia --- configfs.c | 43 +++++++++++++++++++----- core.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++------------ core.h | 21 +++++++++++- sound/avirt.h | 2 ++ 4 files changed, 140 insertions(+), 29 deletions(-) diff --git a/configfs.c b/configfs.c index bc10efb..2751d66 100644 --- a/configfs.c +++ b/configfs.c @@ -59,6 +59,7 @@ if ((tmp > INT_MAX) || (tmp == 0)) \ return -ERANGE; \ s->channels = tmp; \ + snd_avirt_##type##_try_complete(s); \ return count; \ } \ CONFIGFS_ATTR(cfg_snd_avirt_##type##_, channels); @@ -140,12 +141,30 @@ static ssize_t cfg_snd_avirt_stream_map_show(struct config_item *item, static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item, const char *page, size_t count) { - char *split; + char *map; + struct snd_avirt_audiopath *audiopath; struct snd_avirt_stream *stream = snd_avirt_stream_from_config_item(item); - split = strsep((char **)&page, "\n"); - snd_avirt_stream_set_map(stream, split); + map = strsep((char **)&page, "\n"); + + /* If already sealed, we cannot create the stream */ + if (snd_avirt_streams_sealed()) { + D_ERRORK("Streams already sealed. Cannot set map: '%s'", map); + return -EPERM; + } + + if (!strcmp(stream->map, map)) + return -1; + + audiopath = snd_avirt_audiopath_get(map); + if (!audiopath) { + D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map); + } + + memcpy(stream->map, (char *)map, strlen(map)); + + snd_avirt_stream_try_complete(stream); return count; } @@ -304,7 +323,8 @@ static struct config_item_type cfg_snd_avirt_route_type = { static struct config_item * cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name) { - char *split; + char *split, *stream_name; + bool internal = false; int direction; struct snd_avirt_stream *stream; @@ -326,16 +346,23 @@ cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name) } // Get stream name, and create PCM for stream - split = strsep((char **)&name, "\n"); - stream = snd_avirt_stream_create(split, direction); + stream_name = strsep((char **)&name, "\n"); + + // If internal, get internal + split = strstr(stream_name, "__internal"); + if (split) { + stream_name = strsep((char **)&stream_name, "__"); + internal = true; + } + + // Finally, create stream + stream = snd_avirt_stream_create(stream_name, direction, internal); if (IS_ERR(stream)) return ERR_PTR(PTR_ERR(stream)); config_item_init_type_name(&stream->item, name, &cfg_snd_avirt_stream_type); - D_INFOK("Make stream: %s", stream->name); - return &stream->item; } diff --git a/core.c b/core.c index 41d6b40..d66ee2a 100644 --- a/core.c +++ b/core.c @@ -77,8 +77,14 @@ static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) capture = true; } - err = snd_pcm_new(core.card, stream->name, stream->device, playback, - capture, &pcm); + if (stream->internal) { + err = snd_pcm_new_internal(core.card, stream->name, + stream->device, playback, capture, + &pcm); + } else { + err = snd_pcm_new(core.card, stream->name, stream->device, + playback, capture, &pcm); + } if (err < 0) { D_ERRORK("Failed to create PCM device for stream: '%s'", @@ -122,29 +128,56 @@ static struct snd_avirt_route *snd_avirt_route_get(const char *uid) return NULL; } -int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map) +/** + * int snd_avirt_route_try_complete - Set up remaining parameters for a route. + * Channels, sink, and source Audio Paths + * should be set when calling this function. + * @stream: The route to attempt to finalize parameters for. + * @return: 0 on success, negative ERRNO on failure + */ +int snd_avirt_route_try_complete(struct snd_avirt_route *route) +{ + return 0; +} + +/** + * int snd_avirt_stream_try_complete - Set up remaining parameters for a stream. + * Channels and map should be set when + * calling this function. + * @stream: The stream to attempt to finalize parameters for. + * @return: 0 on success, negative ERRNO on failure + */ +int snd_avirt_stream_try_complete(struct snd_avirt_stream *stream) { struct snd_avirt_audiopath *audiopath; + struct snd_avirt_route *route; struct snd_pcm_ops *pcm_ops_ap; - /* If already sealed, we cannot create the stream */ - if (snd_avirt_streams_sealed()) { - D_ERRORK("Streams already sealed. Cannot set map: '%s'", map); + if (snd_avirt_streams_sealed()) return -EPERM; - } - if (!strcmp(stream->map, map)) - return -1; + if ((stream->channels == 0) || (!strcmp(stream->map, "none"))) + return -EPERM; - memcpy(stream->map, (char *)map, strlen(map)); - audiopath = snd_avirt_audiopath_get(map); + audiopath = snd_avirt_audiopath_get(stream->map); if (!audiopath) { D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map); - return -EFAULT; } /* Check for any routes that have been created for this stream */ - stream->route = snd_avirt_route_get(stream->name); + route = snd_avirt_route_get(stream->name); + if (route) { + if (audiopath == route->endpoint_ap[SND_AVIRT_ROUTE_SOURCE]) + route->endpoint_stream[SND_AVIRT_ROUTE_SOURCE] = stream; + else if (audiopath == route->endpoint_ap[SND_AVIRT_ROUTE_SINK]) + route->endpoint_stream[SND_AVIRT_ROUTE_SINK] = stream; + else { + D_INFOK("Cannot set route. Audio Path not compatible"); + return -EPERM; + } + + stream->route = route; + } /* Set up PCM ops */ if (!stream->direction) @@ -348,11 +381,21 @@ int snd_avirt_audiopath_set_private_data(struct snd_avirt_audiopath *ap, { int err = 0; snd_avirt_route_endpoint endpoint = SND_AVIRT_ROUTE_SOURCE; - struct snd_avirt_private_data *avirt_private_data = pcm->private_data; + struct snd_avirt_stream *stream; + struct snd_avirt_private_data *avirt_private_data; + + stream = snd_avirt_stream_find_by_device(pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + goto exit_err; err = snd_avirt_route_endpoint_pos(pcm, ap->uid, &endpoint); if (err == -2) goto exit_err; + + if (stream->internal && stream->route) + pcm = stream->route->endpoint_stream[!endpoint]->pcm; + + avirt_private_data = pcm->private_data; if (!avirt_private_data) goto exit_err; @@ -361,6 +404,8 @@ int snd_avirt_audiopath_set_private_data(struct snd_avirt_audiopath *ap, return 0; exit_err: + D_ERRORK("Error setting private data for ap:%s, stream:%s, endpoint:%d", + ap->uid, pcm->name, endpoint); return -1; } EXPORT_SYMBOL_GPL(snd_avirt_audiopath_set_private_data); @@ -376,17 +421,29 @@ void *snd_avirt_audiopath_get_private_data(struct snd_avirt_audiopath *ap, { int err = 0; snd_avirt_route_endpoint endpoint = SND_AVIRT_ROUTE_SOURCE; - struct snd_avirt_private_data *avirt_private_data = pcm->private_data; + struct snd_avirt_stream *stream; + struct snd_avirt_private_data *avirt_private_data; + + stream = snd_avirt_stream_find_by_device(pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + return NULL; err = snd_avirt_route_endpoint_pos(pcm, ap->uid, &endpoint); if (err == -2) goto exit_err; + + if (stream->internal && stream->route) + pcm = stream->route->endpoint_stream[!endpoint]->pcm; + + avirt_private_data = pcm->private_data; if (!avirt_private_data) goto exit_err; return avirt_private_data->ap_private_data[endpoint]; exit_err: + D_ERRORK("Error getting private data for ap:%s, stream:%s, endpoint:%d", + ap->uid, pcm->name, endpoint); return NULL; } EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get_private_data); @@ -485,10 +542,11 @@ struct snd_avirt_route *snd_avirt_route_create(const char *uid, int direction) * @name: The name designated to the audio stream * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or * SNDRV_PCM_STREAM_CAPTURE) + * @internal: Whether the PCM should be internal or not * @return: The newly created audio stream if successful, or an error pointer */ struct snd_avirt_stream *snd_avirt_stream_create(const char *name, - int direction) + int direction, bool internal) { struct snd_avirt_stream *stream; @@ -505,6 +563,7 @@ struct snd_avirt_stream *snd_avirt_stream_create(const char *name, strncpy(stream->map, "none", MAX_NAME_LEN); stream->channels = 0; stream->direction = direction; + stream->internal = internal; stream->device = core.stream_count++; /* Initialize PCM ops table for this stream. @@ -516,7 +575,12 @@ struct snd_avirt_stream *snd_avirt_stream_create(const char *name, } memcpy(stream->pcm_ops, &pcm_ops_avirt, sizeof(struct snd_pcm_ops)); - D_INFOK("name:%s device:%d", name, stream->device); + if (stream->internal) { + D_INFOK("name:%s device:%d internal:%d", name, stream->device, + stream->internal); + } else { + D_INFOK("name:%s device:%d", name, stream->device); + } return stream; } @@ -540,9 +604,8 @@ int snd_avirt_streams_seal(void) 0) continue; - D_INFOK("Do configure for AP: %s streams:%d cb:%p", - ap_obj->path->uid, stream_array.count, - ap_obj->path->configure); + D_INFOK("Do configure for AP: %s streams:%d", ap_obj->path->uid, + stream_array.count); ap_obj->path->configure(core.card, &stream_array); } diff --git a/core.h b/core.h index f915c04..38fee35 100644 --- a/core.h +++ b/core.h @@ -99,10 +99,29 @@ struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device); * @name: The name designated to the audio stream * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or * SNDRV_PCM_STREAM_CAPTURE) + * @internal: Whether the PCM should be internal or not * @return: The newly created audio stream if successful, or an error pointer */ struct snd_avirt_stream *snd_avirt_stream_create(const char *name, - int direction); + int direction, bool internal); + +/** + * int snd_avirt_route_try_complete - Set up remaining parameters for a route. + * Channels, sink, and source Audio Paths + * should be set when calling this function. + * @stream: The route to attempt to finalize parameters for. + * @return: 0 on success, negative ERRNO on failure + */ +int snd_avirt_route_try_complete(struct snd_avirt_route *route); + +/** + * int snd_avirt_stream_try_complete - Set up remaining parameters for a stream. + * Channels and map should be set when + * calling this function. + * @stream: The stream to attempt to finalize parameters for. + * @return: 0 on success, negative ERRNO on failure + */ +int snd_avirt_stream_try_complete(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/sound/avirt.h b/sound/avirt.h index ecb0a21..9259d83 100644 --- a/sound/avirt.h +++ b/sound/avirt.h @@ -63,6 +63,7 @@ struct snd_avirt_route { unsigned int channels; /* Route stream channel count */ unsigned int direction; /* Route stream direction */ struct snd_avirt_audiopath *endpoint_ap[2]; /* Source/sink */ + struct snd_avirt_stream *endpoint_stream[2]; /* Router PCM */ struct config_item item; /* configfs item reference */ }; @@ -91,6 +92,7 @@ struct snd_avirt_stream { unsigned int channels; /* Stream channel count */ unsigned int device; /* Stream PCM device no. */ unsigned int direction; /* Stream direction */ + bool internal; /* Stream is internal only? */ struct snd_pcm *pcm; /* ALSA PCM */ struct snd_pcm_ops *pcm_ops; /* ALSA PCM ops */ struct snd_avirt_route *route; /* Associated route, if any */ -- cgit 1.2.3-korg