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 | |
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>
-rw-r--r-- | configfs.c | 153 | ||||
-rw-r--r-- | core.c | 213 | ||||
-rw-r--r-- | core.h | 12 | ||||
-rw-r--r-- | pcm.c | 103 | ||||
-rw-r--r-- | sound/avirt.h | 96 | ||||
-rw-r--r-- | sysfs.c | 28 |
6 files changed, 490 insertions, 115 deletions
@@ -68,6 +68,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) { @@ -98,85 +158,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, }; @@ -202,7 +277,7 @@ static void cfg_snd_avirt_route_release(struct config_item *item) return; } - D_INFOK("Release route: %s", route->name); + D_INFOK("Release route: %s", route->uid); kfree(route); } @@ -297,8 +372,6 @@ 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; } @@ -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++; @@ -128,6 +128,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 @@ -15,20 +15,57 @@ #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) @@ -82,8 +119,8 @@ 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); } /** @@ -97,12 +134,44 @@ static int pcm_open(struct snd_pcm_substream *substream) 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 @@ -149,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) @@ -165,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/sound/avirt.h b/sound/avirt.h index 8378004..ecb0a21 100644 --- a/sound/avirt.h +++ b/sound/avirt.h @@ -39,6 +39,34 @@ typedef int (*snd_avirt_audiopath_configure)( 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 config_item item; /* configfs item reference */ +}; + +/** * AVIRT Audio Path info */ typedef struct snd_avirt_audiopath snd_avirt_audiopath; @@ -50,24 +78,8 @@ 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_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; }; /** @@ -81,6 +93,7 @@ struct snd_avirt_stream { unsigned int direction; /* Stream direction */ 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 +114,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,4 +142,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_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. + */ +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, }; |