diff options
author | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-03-27 17:47:26 +1100 |
---|---|---|
committer | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-03-27 17:49:12 +1100 |
commit | c22d65351d533a2077caae117e3baf5558d20963 (patch) | |
tree | e61b7162bbbe6939eeb1afddaf53001aba683dab /core.c | |
parent | 07068446e60502bbd4b7b3a5df16a6246183e1ed (diff) |
Rework routing system
- Add 'route' to snd_avirt_stream, remove from audio path
- Add route checking - ensure that source ap and ink ap have
compatible hw params
- Add private data support for both source and sink Audio
Paths, to ensure that the PCM can hold multiple private data(s).
- Add ability to use copy_kernel and exttriggers from AVIRT instead of from Audio Paths
- Reintroduce pcm_trigger and pcm_prepare, so that
they may be called appropriately from AVIRT, rather
than child Audio Paths.
Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
Diffstat (limited to 'core.c')
-rw-r--r-- | core.c | 213 |
1 files changed, 201 insertions, 12 deletions
@@ -51,7 +51,7 @@ static void pcm_private_free(struct snd_pcm *pcm) /* Free Audio Path private data */ avirt_private_data = (struct snd_avirt_private_data *)pcm->private_data; if (avirt_private_data) { - if (avirt_private_data->ap_private_data && + if (avirt_private_data->ap_private_data[0] && avirt_private_data->ap_private_free) avirt_private_data->ap_private_free(pcm); } @@ -94,7 +94,7 @@ static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, stream->pcm_ops); pcm->info_flags = 0; - strcpy(pcm->name, stream->name); + strncpy(pcm->name, stream->name, MAX_NAME_LEN); avirt_private_data = kzalloc(sizeof(*avirt_private_data), GFP_KERNEL); pcm->private_data = avirt_private_data; @@ -106,6 +106,22 @@ static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) return pcm; } +static struct snd_avirt_route *snd_avirt_route_get(const char *uid) +{ + struct list_head *entry; + struct config_item *item; + struct snd_avirt_route *route; + + list_for_each (entry, &core.route_group->cg_children) { + item = container_of(entry, struct config_item, ci_entry); + route = snd_avirt_route_from_config_item(item); + if (!strcmp(route->uid, uid)) + return route; + } + + return NULL; +} + int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map) { struct snd_avirt_audiopath *audiopath; @@ -127,6 +143,10 @@ int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map) return -EFAULT; } + /* Check for any routes that have been created for this stream */ + stream->route = snd_avirt_route_get(stream->name); + + /* Set up PCM ops */ if (!stream->direction) pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops; else @@ -140,8 +160,6 @@ int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map) } /* Set PCM ops for the Audio Path*/ - PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, prepare); - PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, trigger); PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, pointer); PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, get_time_info); PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, fill_silence); @@ -185,9 +203,124 @@ static int snd_avirt_streams_get(const char *map, } /** - * snd_avirt_audiopath_get - retrieves the Audio Path by its UID - * @uid: Unique ID for the Audio Path - * @return: Corresponding Audio Path + * snd_avirt_route_endpoint_pos - get route endpoint position for Audio Path + * @pcm: The PCM whose route to inspect. + * @ap_uid: The Audio Path UID to get + * @endpoint: The route position (SND_AVIRT_ROUTE_SOURCE or SND_AVIRT_ROUTE_SINK) + * of the Audio Path in it's route (if any). + * @return: 0 if an Audio Path is found in the route, -1 if there is no route, + * or -2 otherwise. + */ +int snd_avirt_route_endpoint_pos(struct snd_pcm *pcm, const char *ap_uid, + snd_avirt_route_endpoint *endpoint) +{ + struct snd_avirt_audiopath *endpoint_ap; + struct snd_avirt_stream *stream; + int i; + + stream = snd_avirt_stream_find_by_device(pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + goto exit_err; + + if (!stream->route) + return -1; + + for (i = 0; i < 2; i++) { + endpoint_ap = stream->route->endpoint_ap[i]; + if (endpoint_ap) + if (!strcmp(endpoint_ap->uid, ap_uid)) { + *endpoint = i; + return 0; + } + } + +exit_err: + D_ERRORK("Could not find Audio Path '%s' in route '%s'", ap_uid, + stream->route->uid); + return -2; +} + +/** + * snd_avirt_route_endpoint_copy - get endpoint copy function for a given + * Audio Path's source/sink. + * @ap: The Audio Path whose endpoint copy function to find. + * @endpoint: The endpoint (SND_AVIRT_ROUTE_SOURCE or SND_AVIRT_ROUTE_SINK). + * @return: A snd_pcm_copy_kernel function pointer that can be used to either: + * 1. Source PCM data into the Audio Path, or, + * 2. Sink PCM data out of the Audio Path. + * If no Audio Path endpoint is routed for 'endpoint', NULL is returned. + */ +snd_pcm_copy_kernel +snd_avirt_route_endpoint_copy(struct snd_pcm_substream *substream, + snd_avirt_route_endpoint endpoint) +{ + struct snd_avirt_audiopath *endpoint_ap; + struct snd_avirt_stream *stream; + + if (endpoint < 0 || endpoint > 1) { + D_ERRORK("Route endpoint must be 0 or 1"); + return NULL; + } + + stream = snd_avirt_stream_find_by_device(substream->pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + return NULL; + + if (!stream->route) + return NULL; + endpoint_ap = stream->route->endpoint_ap[endpoint]; + if (!endpoint_ap) + return NULL; + + switch (endpoint) { + case SND_AVIRT_ROUTE_SOURCE: + return endpoint_ap->pcm_capture_ops->copy_kernel; + case SND_AVIRT_ROUTE_SINK: + return endpoint_ap->pcm_playback_ops->copy_kernel; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_avirt_route_endpoint_copy); + +/** + * snd_avirt_route_endpoint_trigger - Trigger an Audio Path's endpoint + * (sink/source). + * @uid: The Audio Path whose endpoint trigger function to call. + * @endpoint: The endpoint (SND_AVIRT_ROUTE_SOURCE or SND_AVIRT_ROUTE_SINK). + * @return: 0 on success or -1 on failure. + */ +int snd_avirt_route_endpoint_trigger(struct snd_pcm_substream *substream, + snd_avirt_route_endpoint endpoint) +{ + struct snd_avirt_audiopath *endpoint_ap; + struct snd_avirt_stream *stream; + + if (endpoint < 0 || endpoint > 1) { + D_ERRORK("Route endpoint must be 0 or 1"); + return -1; + } + + stream = snd_avirt_stream_find_by_device(substream->pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + return -1; + + if (!stream->route) + return -1; + endpoint_ap = stream->route->endpoint_ap[endpoint]; + if (!endpoint_ap) + return -1; + + endpoint_ap->pcm_exttrigger(); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_avirt_route_endpoint_trigger); + +/** + * snd_avirt_audiopath_get - get Audio Path by it's UID + * @uid: The Audio Path UID to get + * @return: The Audio Path if it exists, NULL otherwise. */ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid) { @@ -202,6 +335,62 @@ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid) } EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get); +/* + * snd_avirt_audiopath_set_private_data - set PCM private data for an Audio Path + * @ap: The Audio Path whose private data to set. + * @pcm: The PCM where the private data is stored. + * @ap_private_data: The value to set to the private data. + * @return: 0 on success, -1 on failure. + */ +int snd_avirt_audiopath_set_private_data(struct snd_avirt_audiopath *ap, + struct snd_pcm *pcm, + void *ap_private_data) +{ + int err = 0; + snd_avirt_route_endpoint endpoint = SND_AVIRT_ROUTE_SOURCE; + struct snd_avirt_private_data *avirt_private_data = pcm->private_data; + + err = snd_avirt_route_endpoint_pos(pcm, ap->uid, &endpoint); + if (err == -2) + goto exit_err; + if (!avirt_private_data) + goto exit_err; + + avirt_private_data->ap_private_data[endpoint] = ap_private_data; + + return 0; + +exit_err: + return -1; +} +EXPORT_SYMBOL_GPL(snd_avirt_audiopath_set_private_data); + +/* + * snd_avirt_audiopath_get_private_data - get PCM private data for an Audio Path + * @ap: The Audio Path whose private data to get. + * @pcm: The PCM where the private data is stored. + * @return: The value assigned to the private data. + */ +void *snd_avirt_audiopath_get_private_data(struct snd_avirt_audiopath *ap, + struct snd_pcm *pcm) +{ + int err = 0; + snd_avirt_route_endpoint endpoint = SND_AVIRT_ROUTE_SOURCE; + struct snd_avirt_private_data *avirt_private_data = pcm->private_data; + + err = snd_avirt_route_endpoint_pos(pcm, ap->uid, &endpoint); + if (err == -2) + goto exit_err; + if (!avirt_private_data) + goto exit_err; + + return avirt_private_data->ap_private_data[endpoint]; + +exit_err: + return NULL; +} +EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get_private_data); + /** * snd_avirt_audiopath_register - register Audio Path with AVIRT * @audiopath: Audio Path to be registered @@ -271,12 +460,12 @@ EXPORT_SYMBOL_GPL(snd_avirt_audiopath_deregister); /** * snd_avirt_route_create - Create audio route - * @name: The name designated to the audio route + * @uid: The unique ID designated to the audio route. * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or * SNDRV_PCM_STREAM_CAPTURE) * @return: The newly created audio route if successful, or an error pointer */ -struct snd_avirt_route *snd_avirt_route_create(const char *name, int direction) +struct snd_avirt_route *snd_avirt_route_create(const char *uid, int direction) { struct snd_avirt_route *route; @@ -284,7 +473,7 @@ struct snd_avirt_route *snd_avirt_route_create(const char *name, int direction) if (!route) return ERR_PTR(-ENOMEM); - strcpy(route->name, name); + strncpy(route->uid, uid, MAX_NAME_LEN); route->channels = 0; route->direction = direction; @@ -312,8 +501,8 @@ struct snd_avirt_stream *snd_avirt_stream_create(const char *name, if (!stream) return ERR_PTR(-ENOMEM); - strcpy(stream->name, name); - strcpy(stream->map, "none"); + strncpy(stream->name, name, MAX_NAME_LEN); + strncpy(stream->map, "none", MAX_NAME_LEN); stream->channels = 0; stream->direction = direction; stream->device = core.stream_count++; |