aboutsummaryrefslogtreecommitdiffstats
path: root/configfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'configfs.c')
-rw-r--r--configfs.c224
1 files changed, 163 insertions, 61 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,
};