diff options
author | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-04-08 16:47:02 +1000 |
---|---|---|
committer | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-04-08 16:47:36 +1000 |
commit | ce0d06930037dd04ad8cc1055c22a3a13466c3e2 (patch) | |
tree | 613decb17e8edcd8f4a812e7d4eecaa7c8aaeb05 | |
parent | 6a2f694005d8e31c74043b9b90c715bcdf394294 (diff) | |
parent | 12c6dc3159cc14cb3456d6d504398ba779538fb3 (diff) |
Merge branch 'master' into backport_4.12
Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
-rw-r--r-- | configfs.c | 224 | ||||
-rw-r--r-- | core.c | 389 | ||||
-rw-r--r-- | core.h | 49 | ||||
-rw-r--r-- | docs/3.Usage.md | 4 | ||||
-rw-r--r-- | dummy/dummy.c | 6 | ||||
-rw-r--r-- | loopback/loopback.c | 15 | ||||
-rw-r--r-- | pcm.c | 125 | ||||
-rwxr-xr-x | scripts/test_configfs_run.sh | 2 | ||||
-rw-r--r-- | sound/avirt.h | 104 | ||||
-rw-r--r-- | sysfs.c | 28 |
10 files changed, 745 insertions, 201 deletions
@@ -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); @@ -68,6 +69,66 @@ CFG_SND_AVIRT_CHANNELS(stream); CFG_SND_AVIRT_DIRECTION_RO(route); CFG_SND_AVIRT_CHANNELS(route); +/* + * Check PCM hw params between a source and a sink + */ +#define CHK_ROUTE_ERR(source_hw, sink_hw, field) \ + do { \ + if (source_hw->field != sink_hw->field) { \ + D_ERRORK( \ + "Route HW mismatch: ##field (src:%d, sink:%d)", \ + source_hw->field, sink_hw->field); \ + return -1; \ + } \ + } while (0) + +/* + * Check the a route's source and sink Audio Path's hardware params, to ensure + * compatibility + */ +static int cfg_snd_avirt_route_verify_hw(struct snd_avirt_audiopath *source_ap, + struct snd_avirt_audiopath *sink_ap) +{ + const struct snd_pcm_hardware *source_hw = source_ap->hw; + const struct snd_pcm_hardware *sink_hw = sink_ap->hw; + + CHK_ROUTE_ERR(source_hw, sink_hw, channels_max); + CHK_ROUTE_ERR(source_hw, sink_hw, channels_min); + CHK_ROUTE_ERR(source_hw, sink_hw, rate_max); + CHK_ROUTE_ERR(source_hw, sink_hw, rate_min); + CHK_ROUTE_ERR(source_hw, sink_hw, rates); + + return 0; +} + +/* + * Store the Audio Path endpoint (source or sink), and check compatiblity + */ +static int cfg_snd_avirt_route_ap_store(struct snd_avirt_route *route, + struct snd_avirt_audiopath *ap, + snd_avirt_route_endpoint endpoint) +{ + /* If other endpoint is set, we want to check that the two Audio Path + * endpoints are compatible before we set this endpoint */ + if (route->endpoint_ap[!endpoint]) { + if (!cfg_snd_avirt_route_verify_hw( + route->endpoint_ap[!endpoint], ap)) { + route->endpoint_ap[endpoint] = ap; + D_INFOK("Route successfully created: '%s' [%s -> %s]", + route->uid, ap->uid, + route->endpoint_ap[!endpoint]->uid); + return 0; + } else { + D_ERRORK("Route could not be created: %s", route->uid); + return -1; + } + } else { + route->endpoint_ap[endpoint] = ap; + } + + return 0; +} + static ssize_t cfg_snd_avirt_stream_map_show(struct config_item *item, char *page) { @@ -80,12 +141,31 @@ 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 configured, we cannot create the stream */ + if (snd_avirt_streams_configured()) { + D_ERRORK("Streams already configured. 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; } @@ -98,85 +178,100 @@ static struct configfs_attribute *cfg_snd_avirt_stream_attrs[] = { NULL, }; -static ssize_t cfg_snd_avirt_route_to_ap_show(struct config_item *item, - char *page) +static ssize_t cfg_snd_avirt_route_sink_ap_show(struct config_item *item, + char *page) { struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); + if (!route) { + D_ERRORK("Cannot get route!"); + goto exit_err; + } - if (route) - if (route->to_ap) - return sprintf(page, "%s\n", route->to_ap->uid); + if (route->endpoint_ap[SND_AVIRT_ROUTE_SINK]) + return sprintf(page, "%s\n", + route->endpoint_ap[SND_AVIRT_ROUTE_SINK]->uid); +exit_err: return sprintf(page, "\n"); } -static ssize_t cfg_snd_avirt_route_to_ap_store(struct config_item *item, - const char *page, size_t count) +static ssize_t cfg_snd_avirt_route_sink_ap_store(struct config_item *item, + const char *page, size_t count) { char *uid_ap; struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); - struct snd_avirt_audiopath *to_ap; + struct snd_avirt_audiopath *sink_ap; + + if (!route) { + D_ERRORK("Cannot get route!"); + goto exit; + } uid_ap = strsep((char **)&page, "\n"); - to_ap = snd_avirt_audiopath_get(uid_ap); - if (!to_ap) { + sink_ap = snd_avirt_audiopath_get(uid_ap); + if (!sink_ap) { D_ERRORK("Audio Path '%s' does not exist!", uid_ap); - D_ERRORK("Cannot set 'route'->'to_ap'"); - return count; + D_ERRORK("Cannot set 'route'->'sink_ap'"); + goto exit; } - route->to_ap = to_ap; - if (route->from_ap) { - route->from_ap->route_to_ap = to_ap; - route->to_ap->route_from_ap = route->from_ap; - } + cfg_snd_avirt_route_ap_store(route, sink_ap, SND_AVIRT_ROUTE_SINK); +exit: return count; } -CONFIGFS_ATTR(cfg_snd_avirt_route_, to_ap); +CONFIGFS_ATTR(cfg_snd_avirt_route_, sink_ap); -static ssize_t cfg_snd_avirt_route_from_ap_show(struct config_item *item, - char *page) +static ssize_t cfg_snd_avirt_route_source_ap_show(struct config_item *item, + char *page) { struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); + if (!route) { + D_ERRORK("Cannot get route!"); + goto exit_err; + } - if (route) - if (route->from_ap) - return sprintf(page, "%s\n", route->from_ap->uid); + if (route->endpoint_ap[SND_AVIRT_ROUTE_SOURCE]) + return sprintf(page, "%s\n", + route->endpoint_ap[SND_AVIRT_ROUTE_SOURCE]->uid); +exit_err: return sprintf(page, "\n"); } -static ssize_t cfg_snd_avirt_route_from_ap_store(struct config_item *item, - const char *page, size_t count) +static ssize_t cfg_snd_avirt_route_source_ap_store(struct config_item *item, + const char *page, + size_t count) { char *uid_ap; struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); - struct snd_avirt_audiopath *from_ap; + struct snd_avirt_audiopath *source_ap; + + if (!route) { + D_ERRORK("Cannot get route!"); + goto exit; + } uid_ap = strsep((char **)&page, "\n"); - from_ap = snd_avirt_audiopath_get(uid_ap); - if (!from_ap) { + source_ap = snd_avirt_audiopath_get(uid_ap); + if (!source_ap) { D_ERRORK("Audio Path '%s' does not exist!", uid_ap); - D_ERRORK("Cannot set 'route'->'from_ap'"); + D_ERRORK("Cannot set 'route'->'source_ap'"); return count; } - route->from_ap = from_ap; - if (route->to_ap) { - route->to_ap->route_from_ap = from_ap; - route->from_ap->route_to_ap = route->to_ap; - } + cfg_snd_avirt_route_ap_store(route, source_ap, SND_AVIRT_ROUTE_SOURCE); +exit: return count; } -CONFIGFS_ATTR(cfg_snd_avirt_route_, from_ap); +CONFIGFS_ATTR(cfg_snd_avirt_route_, source_ap); static struct configfs_attribute *cfg_snd_avirt_route_attrs[] = { &cfg_snd_avirt_route_attr_channels, &cfg_snd_avirt_route_attr_direction, - &cfg_snd_avirt_route_attr_from_ap, - &cfg_snd_avirt_route_attr_to_ap, + &cfg_snd_avirt_route_attr_source_ap, + &cfg_snd_avirt_route_attr_sink_ap, NULL, }; @@ -190,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) @@ -202,8 +296,9 @@ static void cfg_snd_avirt_route_release(struct config_item *item) return; } - D_INFOK("Release route: %s", route->name); kfree(route); + + D_INFOK("Release route: %s", route->uid); } static struct configfs_item_operations cfg_snd_avirt_stream_ops = { @@ -229,7 +324,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; @@ -251,16 +347,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; } @@ -297,39 +400,38 @@ cfg_snd_avirt_route_make_item(struct config_group *group, const char *name) config_item_init_type_name(&route->item, name, &cfg_snd_avirt_route_type); - D_INFOK("Make route: %s", route->name); - return &route->item; } -static ssize_t cfg_snd_avirt_stream_group_sealed_show(struct config_item *item, - char *page) +static ssize_t +cfg_snd_avirt_stream_group_configured_show(struct config_item *item, char *page) { - return snprintf(page, PAGE_SIZE, "%d\n", snd_avirt_streams_sealed()); + return snprintf(page, PAGE_SIZE, "%d\n", + snd_avirt_streams_configured()); } -static ssize_t cfg_snd_avirt_stream_group_sealed_store(struct config_item *item, - const char *page, - size_t count) +static ssize_t +cfg_snd_avirt_stream_group_configured_store(struct config_item *item, + const char *page, size_t count) { unsigned long tmp; char *p = (char *)page; 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; } -CONFIGFS_ATTR(cfg_snd_avirt_stream_group_, sealed); +CONFIGFS_ATTR(cfg_snd_avirt_stream_group_, configured); static struct configfs_attribute *cfg_snd_avirt_stream_group_attrs[] = { - &cfg_snd_avirt_stream_group_attr_sealed, + &cfg_snd_avirt_stream_group_attr_configured, NULL, }; @@ -35,7 +35,7 @@ MODULE_LICENSE("GPL v2"); static struct snd_avirt_core core = { .version = { 0, 0, 1 }, .stream_count = 0, - .streams_sealed = false, + .streams_configured = false, }; static LIST_HEAD(audiopath_list); @@ -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); } @@ -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'", @@ -94,7 +100,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,27 +112,98 @@ static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) return pcm; } -int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map) +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; + 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_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_configured()) 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 */ + 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) pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops; else @@ -140,8 +217,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, silence); @@ -159,6 +234,12 @@ int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map) return 0; } +/** + * snd_avirt_streams_get - Get AVIRT streams for a given Audio Path map + * @map: The Audio Path UID whose streams to find. + * @stream_array: To be populated with streams. + * @return: The number of streams found for the Audio Path. + */ static int snd_avirt_streams_get(const char *map, struct snd_avirt_stream_array *stream_array) { @@ -178,9 +259,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) { @@ -195,6 +391,86 @@ 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_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; + + avirt_private_data->ap_private_data[endpoint] = ap_private_data; + + 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); + +/* + * 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_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); + /** * snd_avirt_audiopath_register - register Audio Path with AVIRT * @audiopath: Audio Path to be registered @@ -222,8 +498,8 @@ int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath) list_add_tail(&audiopath_obj->list, &audiopath_list); - // If we have already sealed the streams, configure this AP - if (core.streams_sealed) { + // If we have already configured the streams, configure this AP + if (core.streams_configured) { stream_array.count = 0; if (snd_avirt_streams_get(audiopath->uid, &stream_array) > 0) audiopath->configure(core.card, &stream_array); @@ -264,12 +540,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; @@ -277,7 +553,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; @@ -289,10 +565,11 @@ struct snd_avirt_route *snd_avirt_route_create(const char *name, 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; @@ -305,10 +582,11 @@ 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->internal = internal; stream->device = core.stream_count++; /* Initialize PCM ops table for this stream. @@ -320,19 +598,24 @@ 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; } -int snd_avirt_streams_seal(void) +int snd_avirt_streams_configure(void) { int err = 0, i = 0; struct snd_avirt_audiopath_obj *ap_obj; struct snd_avirt_stream_array stream_array; - if (core.streams_sealed) { - D_ERRORK("streams are already sealed!"); + if (core.streams_configured) { + D_ERRORK("streams are already configured!"); return -1; } @@ -344,20 +627,50 @@ 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); + if (!ap_obj->path->configure) { + D_ERRORK("Cannot do 'configure' for AP: %s", + ap_obj->path->uid); + return -EFAULT; + } + + D_INFOK("Do 'configure' for AP: %s streams:%d", + ap_obj->path->uid, stream_array.count); ap_obj->path->configure(core.card, &stream_array); } - core.streams_sealed = true; + core.streams_configured = true; return err; } -bool snd_avirt_streams_sealed(void) +int snd_avirt_streams_unconfigure(void) +{ + struct snd_avirt_audiopath_obj *ap_obj; + + 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_sealed; + return core.streams_configured; } struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device) @@ -25,7 +25,7 @@ struct snd_avirt_core { struct config_group *stream_group; struct config_group *route_group; unsigned int stream_count; - bool streams_sealed; + bool streams_configured; }; struct snd_avirt_audiopath_obj { @@ -76,16 +76,18 @@ struct snd_avirt_audiopath_obj *snd_avirt_audiopath_create_obj(const char *uid); void snd_avirt_audiopath_destroy_obj(struct snd_avirt_audiopath_obj *p); /** - * snd_avirt_streams_seal - Register the sound card to user space + * snd_avirt_streams_configure - Register the sound card to user space * @return: 0 on success, negative ERRNO on failure */ -int snd_avirt_streams_seal(void); +int snd_avirt_streams_configure(void); + +int snd_avirt_streams_unconfigure(void); /** - * snd_avirt_streams_sealed - Check if the streams have been sealed or not - * @return: true if sealed, false otherwise + * snd_avirt_streams_configured - Check if streams have been configured or not + * @return: true if configured, false otherwise */ -bool snd_avirt_streams_sealed(void); +bool snd_avirt_streams_configured(void); /** * snd_avirt_stream_find_by_device - Get audio stream from device number @@ -99,10 +101,31 @@ 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); +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. @@ -128,6 +151,18 @@ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid); struct snd_avirt_route *snd_avirt_route_create(const char *name, int direction); /** + * 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); + +/** * snd_avirt_stream_from_config_item - Convert config_item to a snd_avirt_stream * @item: The config_item to convert from * @return: The item's snd_avirt_stream if successful, NULL otherwise diff --git a/docs/3.Usage.md b/docs/3.Usage.md index ba5c3e6..e66b66d 100644 --- a/docs/3.Usage.md +++ b/docs/3.Usage.md @@ -110,8 +110,8 @@ mkdir /config/snd-avirt/streams/capture_voice echo "1">/config/snd-avirt/streams/capture_voice/channels echo "ap_dummy">/config/snd-avirt/streams/capture_voice/map -# Finally, seal the card, and initiate configuration -echo "1">/config/snd-avirt/streams/sealed +# Finally, set the card to 'configured', and initiate Audio Path configuration +echo "1">/config/snd-avirt/streams/configured ``` Alternatively, the test script at `scripts/test_configfs.sh` can be used. diff --git a/dummy/dummy.c b/dummy/dummy.c index f683b92..013fe97 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -31,7 +31,7 @@ MODULE_LICENSE("GPL v2"); #define DUMMY_PERIODS_MIN 1 #define DUMMY_PERIODS_MAX 8 -#define get_dummy_ops(substream) \ +#define get_dummy_ops(substream) \ (*(const struct dummy_timer_ops **)(substream)->runtime->private_data) /******************************************************************************* @@ -138,11 +138,11 @@ static void dummy_systimer_callback(struct timer_list *t) dpcm->elapsed = 0; spin_unlock_irqrestore(&dpcm->lock, flags); if (elapsed) - snd_avirt_pcm_period_elapsed(dpcm->substream); + snd_pcm_period_elapsed(dpcm->substream); } static snd_pcm_uframes_t - dummy_systimer_pointer(struct snd_pcm_substream *substream) +dummy_systimer_pointer(struct snd_pcm_substream *substream) { struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; snd_pcm_uframes_t pos; diff --git a/loopback/loopback.c b/loopback/loopback.c index 3463989..1fbec88 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -508,7 +508,7 @@ static void loopback_timer_function(struct timer_list *t) dpcm->period_update_pending = 0; spin_unlock_irqrestore(&dpcm->cable->lock, flags); /* need to unlock before calling below */ - snd_avirt_pcm_period_elapsed(dpcm->substream); + snd_pcm_period_elapsed(dpcm->substream); return; } } @@ -966,7 +966,7 @@ static int loopback_mixer_new(struct loopback *loopback, int notify) default: break; } - err = snd_ctl_add(card, kctl); + err = snd_ctl_replace(card, kctl, true); if (err < 0) return err; } @@ -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); } @@ -15,38 +15,61 @@ #define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) #define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) +#define AUDIOPATH_PCM_CB(ap, cb, ops, substream, ...) \ + (((ap)->ops->cb) ? (ap)->ops->cb((substream), ##__VA_ARGS__) : 0) + +#define DO_AUDIOPATH_PCM_ROUTE_CBS(ap, cb, ops, substream, ...) \ + ({ \ + int __err = 0, __endpoint = substream->stream; \ + struct snd_avirt_stream *__stream = \ + snd_avirt_stream_find_by_device( \ + substream->pcm->device); \ + if (!IS_ERR_VALUE(__stream) && __stream) { \ + struct snd_avirt_audiopath *__endpoint_ap; \ + if (__stream->route) { \ + __endpoint_ap = \ + __stream->route \ + ->endpoint_ap[__endpoint]; \ + if ((__endpoint_ap)) { \ + __err = AUDIOPATH_PCM_CB( \ + __endpoint_ap, cb, ops, \ + (substream), ##__VA_ARGS__); \ + } \ + } \ + } \ + (__err); \ + }) /** - * DO_AUDIOPATH_CB - Used to call into an Audio Path's ALSA PCM callback from - * AVIRT, where AVIRT's own callback must be attended to first. + * DO_AUDIOPATH_PCM_CBS - Call an Audio Path's ALSA PCM callbacks from AVIRT. + * If the Audio Path has routing enabled, then we call the appropriate + * from/to Audio Path's ALSA PCM callbacks */ -#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ - ((!substream->stream) ? \ - (((ap)->pcm_playback_ops->callback) ? \ - (ap)->pcm_playback_ops->callback((substream), \ - ##__VA_ARGS__) : \ - 0) : \ - (((ap)->pcm_capture_ops->callback) ? \ - (ap)->pcm_capture_ops->callback((substream), \ - ##__VA_ARGS__) : \ - 0)) +#define DO_AUDIOPATH_PCM_CBS(ap, cb, substream, ...) \ + ({ \ + int __err = 0; \ + if (substream->stream) { \ + __err = AUDIOPATH_PCM_CB((ap), cb, pcm_capture_ops, \ + (substream), ##__VA_ARGS__); \ + if (__err >= 0) { \ + __err = DO_AUDIOPATH_PCM_ROUTE_CBS( \ + (ap), cb, pcm_playback_ops, \ + (substream), ##__VA_ARGS__); \ + } \ + } else { \ + __err = AUDIOPATH_PCM_CB((ap), cb, pcm_playback_ops, \ + (substream), ##__VA_ARGS__); \ + if (__err >= 0) { \ + __err = DO_AUDIOPATH_PCM_ROUTE_CBS( \ + (ap), cb, pcm_capture_ops, \ + (substream), ##__VA_ARGS__); \ + } \ + } \ + (__err); \ + }) #define PRIVATE_DATA(substream) \ ((struct snd_avirt_private_data *)substream->private_data) -/** - * snd_avirt_pcm_period_elapsed - PCM buffer complete callback - * @substreamid: pointer to ALSA PCM substream - * - * This should be called from a child Audio Path once it has finished - * processing the pcm buffer - */ -void snd_avirt_pcm_period_elapsed(struct snd_pcm_substream *substream) -{ - // Notify ALSA middle layer of the elapsed period boundary - snd_pcm_period_elapsed(substream); -} -EXPORT_SYMBOL_GPL(snd_avirt_pcm_period_elapsed); - /******************************************************************************* * ALSA PCM Callbacks ******************************************************************************/ @@ -67,8 +90,8 @@ static int pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_hardware *hw; unsigned int chans = 0; - if (!snd_avirt_streams_sealed()) { - D_ERRORK("Cannot open PCM. Card is not sealed"); + if (!snd_avirt_streams_configured()) { + D_ERRORK("Cannot open PCM. Card is not configured"); return -EPERM; } @@ -96,27 +119,59 @@ static int pcm_open(struct snd_pcm_substream *substream) hw->channels_min = chans; hw->channels_max = chans; - // Do additional Audio Path 'open' callback - return DO_AUDIOPATH_CB(audiopath, open, substream); + // Do additional Audio Path 'open' callbacks + return DO_AUDIOPATH_PCM_CBS(audiopath, open, substream); } /** * pcm_close - Implements 'close' callback for PCM middle layer * @substream: pointer to ALSA PCM substream - * + * * This is called when a PCM substream is closed. - * + * * Returns 0 on success or error code otherwise. */ static int pcm_close(struct snd_pcm_substream *substream) { // Do additional Audio Path 'close' callback - return DO_AUDIOPATH_CB( + return DO_AUDIOPATH_PCM_CBS( (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath, close, substream); } /** + * pcm_close - Implements 'close' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This is called when a PCM substream is closed. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + // Do additional Audio Path 'trigger' callback + return DO_AUDIOPATH_PCM_CBS( + (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath, + trigger, substream, cmd); +} + +/** + * pcm_close - Implements 'close' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This is called when a PCM substream is closed. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_prepare(struct snd_pcm_substream *substream) +{ + // Do additional Audio Path 'prepare' callback + return DO_AUDIOPATH_PCM_CBS( + (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath, + prepare, substream); +} + +/** * pcm_hw_params - Implements 'hw_params' callback for PCM middle layer * @substream: pointer to ALSA PCM substream * @hw_params: contains the hardware parameters for the PCM @@ -163,7 +218,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) int err; // Do additional Audio Path 'hw_free' callback - err = DO_AUDIOPATH_CB( + err = DO_AUDIOPATH_PCM_CBS( (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath, hw_free, substream); if (err < 0) @@ -179,6 +234,8 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) struct snd_pcm_ops pcm_ops_avirt = { .open = pcm_open, .close = pcm_close, + .prepare = pcm_prepare, + .trigger = pcm_trigger, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_hw_params, .hw_free = pcm_hw_free, diff --git a/scripts/test_configfs_run.sh b/scripts/test_configfs_run.sh index b6a56f6..a215b33 100755 --- a/scripts/test_configfs_run.sh +++ b/scripts/test_configfs_run.sh @@ -18,4 +18,4 @@ mkdir /config/snd-avirt/streams/capture_voice echo "1">/config/snd-avirt/streams/capture_voice/channels echo "ap_loopback">/config/snd-avirt/streams/capture_voice/map -echo "1">/config/snd-avirt/streams/sealed +echo "1">/config/snd-avirt/streams/configured diff --git a/sound/avirt.h b/sound/avirt.h index 138d0ca..46756b3 100644 --- a/sound/avirt.h +++ b/sound/avirt.h @@ -36,9 +36,40 @@ 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); /** + * ALSA copy_kernel function type + * A standard PCM operation from ALSA. This is used in AVIRT to copy PCM data + * between Audio Paths, along given routes. + */ +typedef int (*snd_pcm_copy_kernel)(struct snd_pcm_substream *substream, + int channel, unsigned long pos, void *buf, + unsigned long bytes); + +/* + * AVIRT route source/sink enumeration types + */ +typedef enum snd_avirt_route_endpoint_t { + SND_AVIRT_ROUTE_SOURCE, + SND_AVIRT_ROUTE_SINK, +} snd_avirt_route_endpoint; + +/** + * Audio routing + */ +struct snd_avirt_route { + char uid[MAX_NAME_LEN]; /* Unique identifier */ + 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 */ +}; + +/** * AVIRT Audio Path info */ typedef struct snd_avirt_audiopath snd_avirt_audiopath; @@ -50,24 +81,9 @@ 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_pcm_exttrigger pcm_exttrigger; + snd_avirt_audiopath_unconfigure unconfigure; /* Unconfig cb function */ + snd_avirt_pcm_exttrigger pcm_exttrigger; /* External trigger callback */ void *context; - - // MUST be at the end - struct snd_avirt_audiopath *route_from_ap; - struct snd_avirt_audiopath *route_to_ap; -}; - -/** - * Audio routing - */ -struct snd_avirt_route { - char name[MAX_NAME_LEN]; - unsigned int channels; - unsigned int direction; - struct snd_avirt_audiopath *from_ap; - struct snd_avirt_audiopath *to_ap; - struct config_item item; }; /** @@ -79,8 +95,10 @@ 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 */ struct config_item item; /* configfs item reference */ }; @@ -101,9 +119,10 @@ typedef void (*snd_avirt_ap_private_free)(struct snd_pcm *pcm); * Private Data Expansion */ struct snd_avirt_private_data { + /* Initial Audio Path in the route */ struct snd_avirt_audiopath *audiopath; - void *ap_private_data; - + /* Private data for source/sink Audio Paths */ + void *ap_private_data[2]; snd_avirt_ap_private_free ap_private_free; }; @@ -128,13 +147,48 @@ int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath); */ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid); +/* + * 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); + +/* + * 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); + /** - * snd_avirt_pcm_period_elapsed - PCM buffer complete callback - * @substream: pointer to ALSA PCM substream - * - * This should be called from a child Audio Path once it has finished processing - * the PCM buffer + * 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); + +/** + * 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. */ -void snd_avirt_pcm_period_elapsed(struct snd_pcm_substream *substream); +int snd_avirt_route_endpoint_trigger(struct snd_pcm_substream *substream, + snd_avirt_route_endpoint endpoint); #endif // __SOUND_AVIRT_H @@ -115,42 +115,14 @@ static ssize_t version_show(struct snd_avirt_audiopath_obj *audiopath_obj, audiopath->version[1], audiopath->version[2]); } -static ssize_t route_from_ap_show(struct snd_avirt_audiopath_obj *audiopath_obj, - struct snd_avirt_audiopath_attribute *attr, - char *buf) -{ - struct snd_avirt_audiopath *audiopath = audiopath_obj->path; - - if (audiopath->route_from_ap) - return sprintf(buf, "%s\n", audiopath->route_from_ap->uid); - - return 0; -} - -static ssize_t route_to_ap_show(struct snd_avirt_audiopath_obj *audiopath_obj, - struct snd_avirt_audiopath_attribute *attr, - char *buf) -{ - struct snd_avirt_audiopath *audiopath = audiopath_obj->path; - - if (audiopath->route_to_ap) - return sprintf(buf, "%s\n", audiopath->route_to_ap->uid); - - return 0; -} - static struct snd_avirt_audiopath_attribute snd_avirt_audiopath_attrs[] = { __ATTR_RO(name), __ATTR_RO(version), - __ATTR_RO(route_from_ap), - __ATTR_RO(route_to_ap), }; static struct attribute *snd_avirt_audiopath_def_attrs[] = { &snd_avirt_audiopath_attrs[0].attr, &snd_avirt_audiopath_attrs[1].attr, - &snd_avirt_audiopath_attrs[2].attr, - &snd_avirt_audiopath_attrs[3].attr, NULL, }; |