aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-04-08 16:47:02 +1000
committerMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-04-08 16:47:36 +1000
commitce0d06930037dd04ad8cc1055c22a3a13466c3e2 (patch)
tree613decb17e8edcd8f4a812e7d4eecaa7c8aaeb05
parent6a2f694005d8e31c74043b9b90c715bcdf394294 (diff)
parent12c6dc3159cc14cb3456d6d504398ba779538fb3 (diff)
Merge branch 'master' into backport_4.12
Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
-rw-r--r--configfs.c224
-rw-r--r--core.c389
-rw-r--r--core.h49
-rw-r--r--docs/3.Usage.md4
-rw-r--r--dummy/dummy.c6
-rw-r--r--loopback/loopback.c15
-rw-r--r--pcm.c125
-rwxr-xr-xscripts/test_configfs_run.sh2
-rw-r--r--sound/avirt.h104
-rw-r--r--sysfs.c28
10 files changed, 745 insertions, 201 deletions
diff --git a/configfs.c b/configfs.c
index 5668cb3..8f2d452 100644
--- a/configfs.c
+++ b/configfs.c
@@ -59,6 +59,7 @@
if ((tmp > INT_MAX) || (tmp == 0)) \
return -ERANGE; \
s->channels = tmp; \
+ snd_avirt_##type##_try_complete(s); \
return count; \
} \
CONFIGFS_ATTR(cfg_snd_avirt_##type##_, channels);
@@ -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,
};
diff --git a/core.c b/core.c
index 2c643fc..c03bf1c 100644
--- a/core.c
+++ b/core.c
@@ -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)
diff --git a/core.h b/core.h
index e4fb7a4..c39002a 100644
--- a/core.h
+++ b/core.h
@@ -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);
}
diff --git a/pcm.c b/pcm.c
index e91b121..8fec3f0 100644
--- a/pcm.c
+++ b/pcm.c
@@ -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
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,
};