summaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--configfs.c254
-rw-r--r--core.c22
-rw-r--r--core.h21
-rw-r--r--sound/avirt.h21
-rw-r--r--sysfs.c27
5 files changed, 305 insertions, 40 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:
diff --git a/core.c b/core.c
index fd74b08..e1b056e 100644
--- a/core.c
+++ b/core.c
@@ -266,6 +266,28 @@ int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath)
EXPORT_SYMBOL_GPL(snd_avirt_audiopath_deregister);
/**
+ * snd_avirt_route_create - Create audio route
+ * @name: The name 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 *route;
+
+ route = kzalloc(sizeof(*route), GFP_KERNEL);
+ if (!route)
+ return ERR_PTR(-ENOMEM);
+
+ strcpy(route->name, name);
+ route->channels = 0;
+ route->direction = direction;
+
+ return route;
+}
+
+/**
* snd_avirt_stream_create - Create audio stream, including it's ALSA PCM device
* @name: The name designated to the audio stream
* @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or
diff --git a/core.h b/core.h
index e83631f..e4fb7a4 100644
--- a/core.h
+++ b/core.h
@@ -23,6 +23,7 @@ struct snd_avirt_core {
struct class *class;
struct platform_device *plat_dev;
struct config_group *stream_group;
+ struct config_group *route_group;
unsigned int stream_count;
bool streams_sealed;
};
@@ -118,6 +119,15 @@ int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map);
struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid);
/**
+ * snd_avirt_route_create - Create audio route
+ * @name: The name 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);
+
+/**
* 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
@@ -128,4 +138,15 @@ snd_avirt_stream_from_config_item(struct config_item *item)
return item ? container_of(item, struct snd_avirt_stream, item) : NULL;
}
+/**
+ * snd_avirt_route_from_config_item - Convert config_item to a snd_avirt_route
+ * @item: The config_item to convert from
+ * @return: The item's snd_avirt_stream if successful, NULL otherwise
+ */
+static inline struct snd_avirt_route *
+snd_avirt_route_from_config_item(struct config_item *item)
+{
+ return item ? container_of(item, struct snd_avirt_route, item) : NULL;
+}
+
#endif /* __SOUND_AVIRT_CORE_H */
diff --git a/sound/avirt.h b/sound/avirt.h
index cb9a61e..138d0ca 100644
--- a/sound/avirt.h
+++ b/sound/avirt.h
@@ -36,9 +36,12 @@ 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 void (*snd_avirt_pcm_exttrigger)(void);
+
/**
* AVIRT Audio Path info
*/
+typedef struct snd_avirt_audiopath snd_avirt_audiopath;
struct snd_avirt_audiopath {
const char *uid; /* Unique identifier */
const char *name; /* Pretty name */
@@ -47,8 +50,24 @@ 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;
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;
};
/**
diff --git a/sysfs.c b/sysfs.c
index b7d28f5..29f9b5d 100644
--- a/sysfs.c
+++ b/sysfs.c
@@ -115,15 +115,42 @@ 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,
};