summaryrefslogtreecommitdiffstats
path: root/configfs.c
diff options
context:
space:
mode:
authorMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-03-01 17:32:37 +1100
committerMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-03-05 10:33:28 +1100
commitda4c2a5619c60cd4dab733bb23a33ed373c78c77 (patch)
treeadcddd53e1981b5a31aa721b8fb98d24ec4d5958 /configfs.c
parentdda7b36122998602a11462398fc0072c0f418bd5 (diff)
Add ability to route audio between audio paths
Configfs interface for adding 'routes' is added, and allows two audio paths to chain together particular inputs/outputs, allowing for multistage audio driver handling. A particular use-case is that of an ADSP driver. We can feed in audio to it, and capture the processed, and/or mixed, audio at it's output, and direct to another audio path driver, such as the UNICENS audio driver. Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
Diffstat (limited to 'configfs.c')
-rw-r--r--configfs.c254
1 files changed, 215 insertions, 39 deletions
diff --git a/configfs.c b/configfs.c
index 4792f87..58e523f 100644
--- a/configfs.c
+++ b/configfs.c
@@ -17,18 +17,56 @@
#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args)
#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args)
-static ssize_t cfg_snd_avirt_stream_direction_show(struct config_item *item,
- char *page)
-{
- ssize_t count;
- struct snd_avirt_stream *stream =
- snd_avirt_stream_from_config_item(item);
-
- count = sprintf(page, "%d\n", stream->direction);
-
- return count;
-}
-CONFIGFS_ATTR_RO(cfg_snd_avirt_stream_, direction);
+/**
+ * Defines the configfs direction 'show' callback for a stream or route
+ */
+#define CFG_SND_AVIRT_DIRECTION_RO(type) \
+ static ssize_t cfg_snd_avirt_##type##_direction_show( \
+ struct config_item *item, char *page) \
+ { \
+ ssize_t count; \
+ struct snd_avirt_##type *s = \
+ snd_avirt_##type##_from_config_item(item); \
+ count = sprintf(page, "%d\n", s->direction); \
+ return count; \
+ } \
+ CONFIGFS_ATTR_RO(cfg_snd_avirt_##type##_, direction)
+
+/**
+ * Defines the configfs channels 'show'/'store' callbacks for a stream or route
+ */
+#define CFG_SND_AVIRT_CHANNELS(type) \
+ static ssize_t cfg_snd_avirt_##type##_channels_show( \
+ struct config_item *item, char *page) \
+ { \
+ ssize_t count; \
+ struct snd_avirt_##type *s = \
+ snd_avirt_##type##_from_config_item(item); \
+ count = sprintf(page, "%d\n", s->channels); \
+ return count; \
+ } \
+ static ssize_t cfg_snd_avirt_##type##_channels_store( \
+ struct config_item *item, const char *page, size_t count) \
+ { \
+ int err; \
+ struct snd_avirt_##type *s = \
+ snd_avirt_##type##_from_config_item(item); \
+ unsigned long tmp; \
+ char *p = (char *)page; \
+ err = kstrtoul(p, 10, &tmp); \
+ if (err < 0) \
+ return err; \
+ if ((tmp > INT_MAX) || (tmp == 0)) \
+ return -ERANGE; \
+ s->channels = tmp; \
+ return count; \
+ } \
+ CONFIGFS_ATTR(cfg_snd_avirt_##type##_, channels);
+
+CFG_SND_AVIRT_DIRECTION_RO(stream);
+CFG_SND_AVIRT_CHANNELS(stream);
+CFG_SND_AVIRT_DIRECTION_RO(route);
+CFG_SND_AVIRT_CHANNELS(route);
static ssize_t cfg_snd_avirt_stream_map_show(struct config_item *item,
char *page)
@@ -53,64 +91,141 @@ static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item,
}
CONFIGFS_ATTR(cfg_snd_avirt_stream_, map);
-static ssize_t cfg_snd_avirt_stream_channels_show(struct config_item *item,
- char *page)
+static struct configfs_attribute *cfg_snd_avirt_stream_attrs[] = {
+ &cfg_snd_avirt_stream_attr_channels,
+ &cfg_snd_avirt_stream_attr_map,
+ &cfg_snd_avirt_stream_attr_direction,
+ NULL,
+};
+
+static ssize_t cfg_snd_avirt_route_to_ap_show(struct config_item *item,
+ char *page)
{
- ssize_t count;
- struct snd_avirt_stream *stream =
- snd_avirt_stream_from_config_item(item);
+ struct snd_avirt_route *route = snd_avirt_route_from_config_item(item);
+
+ if (route)
+ if (route->to_ap)
+ return sprintf(page, "%s\n", route->to_ap->uid);
+
+ return sprintf(page, "\n");
+}
+
+static ssize_t cfg_snd_avirt_route_to_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;
+
+ uid_ap = strsep((char **)&page, "\n");
+ to_ap = snd_avirt_audiopath_get(uid_ap);
+ if (!to_ap) {
+ D_ERRORK("Audio Path '%s' does not exist!", uid_ap);
+ D_ERRORK("Cannot set 'route'->'to_ap'");
+ return count;
+ }
- count = sprintf(page, "%d\n", stream->channels);
+ 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;
+ }
return count;
}
+CONFIGFS_ATTR(cfg_snd_avirt_route_, to_ap);
-static ssize_t cfg_snd_avirt_stream_channels_store(struct config_item *item,
- const char *page,
- size_t count)
+static ssize_t cfg_snd_avirt_route_from_ap_show(struct config_item *item,
+ char *page)
{
- int err;
- struct snd_avirt_stream *stream =
- snd_avirt_stream_from_config_item(item);
- unsigned long tmp;
- char *p = (char *)page;
+ struct snd_avirt_route *route = snd_avirt_route_from_config_item(item);
- err = kstrtoul(p, 10, &tmp);
- if (err < 0)
- return err;
+ if (route)
+ if (route->from_ap)
+ return sprintf(page, "%s\n", route->from_ap->uid);
- if ((tmp > INT_MAX) || (tmp == 0))
- return -ERANGE;
+ return sprintf(page, "\n");
+}
- stream->channels = tmp;
+static ssize_t cfg_snd_avirt_route_from_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;
+
+ uid_ap = strsep((char **)&page, "\n");
+ from_ap = snd_avirt_audiopath_get(uid_ap);
+ if (!from_ap) {
+ D_ERRORK("Audio Path '%s' does not exist!", uid_ap);
+ D_ERRORK("Cannot set 'route'->'from_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;
+ }
return count;
}
-CONFIGFS_ATTR(cfg_snd_avirt_stream_, channels);
+CONFIGFS_ATTR(cfg_snd_avirt_route_, from_ap);
-static struct configfs_attribute *cfg_snd_avirt_stream_attrs[] = {
- &cfg_snd_avirt_stream_attr_channels,
- &cfg_snd_avirt_stream_attr_map,
- &cfg_snd_avirt_stream_attr_direction,
+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,
NULL,
};
static void cfg_snd_avirt_stream_release(struct config_item *item)
{
- D_INFOK("item->name:%s", item->ci_namebuf);
- kfree(snd_avirt_stream_from_config_item(item));
+ struct snd_avirt_stream *stream =
+ snd_avirt_stream_from_config_item(item);
+ if (!stream) {
+ D_ERRORK("Cannot release stream!");
+ return;
+ }
+
+ D_INFOK("Release stream: %s", stream->name);
+ kfree(stream->pcm_ops);
+ kfree(stream);
+}
+
+static void cfg_snd_avirt_route_release(struct config_item *item)
+{
+ struct snd_avirt_route *route = snd_avirt_route_from_config_item(item);
+ if (!route) {
+ D_ERRORK("Cannot release route!");
+ return;
+ }
+
+ D_INFOK("Release route: %s", route->name);
+ kfree(route);
}
static struct configfs_item_operations cfg_snd_avirt_stream_ops = {
.release = cfg_snd_avirt_stream_release,
};
+static struct configfs_item_operations cfg_snd_avirt_route_ops = {
+ .release = cfg_snd_avirt_route_release,
+};
+
static struct config_item_type cfg_snd_avirt_stream_type = {
.ct_item_ops = &cfg_snd_avirt_stream_ops,
.ct_attrs = cfg_snd_avirt_stream_attrs,
.ct_owner = THIS_MODULE,
};
+static struct config_item_type cfg_snd_avirt_route_type = {
+ .ct_item_ops = &cfg_snd_avirt_route_ops,
+ .ct_attrs = cfg_snd_avirt_route_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
static struct config_item *
cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name)
{
@@ -144,9 +259,49 @@ cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name)
config_item_init_type_name(&stream->item, name,
&cfg_snd_avirt_stream_type);
+ D_INFOK("Make stream: %s", stream->name);
+
return &stream->item;
}
+static struct config_item *
+cfg_snd_avirt_route_make_item(struct config_group *group, const char *name)
+{
+ char *split;
+ int direction;
+ struct snd_avirt_route *route;
+
+ // Get prefix (playback_ or capture_)
+ split = strsep((char **)&name, "_");
+ if (!split) {
+ D_ERRORK("Route name: '%s' invalid!", split);
+ D_ERRORK("Must begin with playback_ * or capture_ *");
+ return ERR_PTR(-EINVAL);
+ }
+ if (!strcmp(split, "playback")) {
+ direction = SNDRV_PCM_STREAM_PLAYBACK;
+ } else if (!strcmp(split, "capture")) {
+ direction = SNDRV_PCM_STREAM_CAPTURE;
+ } else {
+ D_ERRORK("Route name: '%s' invalid!", split);
+ D_ERRORK("Must begin with playback_ * or capture_ ");
+ return ERR_PTR(-EINVAL);
+ }
+
+ // Get route name, and create route
+ split = strsep((char **)&name, "\n");
+ route = snd_avirt_route_create(split, direction);
+ if (IS_ERR(route))
+ return ERR_PTR(PTR_ERR(route));
+
+ 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)
{
@@ -182,12 +337,22 @@ static struct configfs_group_operations cfg_snd_avirt_stream_group_ops = {
.make_item = cfg_snd_avirt_stream_make_item
};
+static struct configfs_group_operations cfg_snd_avirt_route_group_ops = {
+ .make_item = cfg_snd_avirt_route_make_item
+};
+
static struct config_item_type cfg_stream_group_type = {
.ct_group_ops = &cfg_snd_avirt_stream_group_ops,
.ct_attrs = cfg_snd_avirt_stream_group_attrs,
.ct_owner = THIS_MODULE
};
+static struct config_item_type cfg_route_group_type = {
+ .ct_group_ops = &cfg_snd_avirt_route_group_ops,
+ .ct_attrs = NULL,
+ .ct_owner = THIS_MODULE
+};
+
static struct config_item_type cfg_avirt_group_type = {
.ct_owner = THIS_MODULE,
};
@@ -212,6 +377,8 @@ int __init snd_avirt_configfs_init(struct snd_avirt_core *core)
D_ERRORK("Cannot register configfs subsys!");
return err;
}
+
+ /* Create streams default group */
core->stream_group = configfs_register_default_group(
&cfg_subsys.su_group, "streams", &cfg_stream_group_type);
if (IS_ERR(core->stream_group)) {
@@ -220,6 +387,15 @@ int __init snd_avirt_configfs_init(struct snd_avirt_core *core)
goto exit_configfs;
}
+ /* Create routes default group */
+ core->route_group = configfs_register_default_group(
+ &cfg_subsys.su_group, "routes", &cfg_route_group_type);
+ if (IS_ERR(core->route_group)) {
+ err = PTR_ERR(core->route_group);
+ D_ERRORK("Cannot register configfs default group 'routes'!");
+ goto exit_configfs;
+ }
+
return 0;
exit_configfs: