aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-03-27 17:47:26 +1100
committerMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-03-27 17:49:12 +1100
commitc22d65351d533a2077caae117e3baf5558d20963 (patch)
treee61b7162bbbe6939eeb1afddaf53001aba683dab
parent07068446e60502bbd4b7b3a5df16a6246183e1ed (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.c153
-rw-r--r--core.c213
-rw-r--r--core.h12
-rw-r--r--pcm.c103
-rw-r--r--sound/avirt.h96
-rw-r--r--sysfs.c28
6 files changed, 490 insertions, 115 deletions
diff --git a/configfs.c b/configfs.c
index 5668cb3..bc10efb 100644
--- a/configfs.c
+++ b/configfs.c
@@ -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;
}
diff --git a/core.c b/core.c
index f7e7978..41d6b40 100644
--- a/core.c
+++ b/core.c
@@ -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++;
diff --git a/core.h b/core.h
index e4fb7a4..f915c04 100644
--- a/core.h
+++ b/core.h
@@ -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
diff --git a/pcm.c b/pcm.c
index fc01953..32456cd 100644
--- a/pcm.c
+++ b/pcm.c
@@ -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
diff --git a/sysfs.c b/sysfs.c
index 29f9b5d..23132ce 100644
--- a/sysfs.c
+++ b/sysfs.c
@@ -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,
};