summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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,
};