aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-03-05 12:35:47 +1100
committerMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-03-05 12:36:02 +1100
commit6a2f694005d8e31c74043b9b90c715bcdf394294 (patch)
tree9cd08c4772c2e13d5cff4e3ad49c6cce3f68be77
parentea9a8ede7f343d589c8d9fac945f7b3d9dca9c3f (diff)
parent9f68614f664506535c873699dcfbca6e2515a24c (diff)
Merge remote-tracking branch 'agl/master' into backport_4.12
Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
-rw-r--r--.vscode/settings.json13
-rw-r--r--.vscode/tasks.json142
-rw-r--r--Makefile2
-rw-r--r--configfs.c262
-rw-r--r--core.c369
-rw-r--r--core.h62
-rw-r--r--loopback/loopback.c2
-rw-r--r--pcm.c35
-rwxr-xr-xscripts/load.sh2
-rwxr-xr-xscripts/make-agl-xds.sh14
-rwxr-xr-xscripts/make-agl.sh22
-rwxr-xr-xscripts/test_configfs_cleanup.sh6
-rwxr-xr-xscripts/test_configfs_run.sh (renamed from scripts/test_configfs.sh)0
-rwxr-xr-xscripts/unload.sh4
-rw-r--r--sound/avirt.h54
-rw-r--r--sysfs.c201
16 files changed, 812 insertions, 378 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 4dc08b0..0839b43 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,8 +1,9 @@
{
- "editor.tabSize": 8,
- "editor.insertSpaces": false,
- "editor.rulers": [
- 80
- ],
- "editor.formatOnSave": true,
+ "editor.tabSize": 8,
+ "editor.insertSpaces": false,
+ "editor.rulers": [
+ 80
+ ],
+ "editor.formatOnSave": true,
+ "editor.trimAutoWhitespace": true
} \ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 999a832..09c6bb5 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -1,63 +1,81 @@
{
- // See https://go.microsoft.com/fwlink/?LinkId=733558
- // for the documentation about the tasks.json format
- "version": "2.0.0",
- "type": "shell",
- "presentation": {
- "reveal": "always"
- },
- "tasks": [
- {
- "label": "Make Driver",
- "command": "make all",
- "group": "build",
- "problemMatcher": ["$gcc"]
- },
- {
- "label": "Clean Driver",
- "command": "make clean",
- "group": "build",
- "problemMatcher": ["$gcc"]
- },
- {
- "label": "Deploy Drivers",
- "command": "sudo sh -c './scripts/unload.sh && ./scripts/load.sh'",
- "problemMatcher": []
- },
- {
- "label": "Make and Deploy Drivers",
- "command": "make all && sudo sh -c './scripts/unload.sh && ./scripts/load.sh'",
- "problemMatcher": []
- },
- {
- "label": "Unload new drivers",
- "command": "sudo sh ./scripts/unload.sh",
- "problemMatcher": []
- },
- {
- "label": "Load new drivers",
- "command": "sudo sh ./scripts/load.sh",
- "problemMatcher": []
- },
- {
- "label": "Make Driver AGL",
- "type": "shell",
- "command": "./scripts/make-agl.sh 6864c558",
- "group": "build",
- "problemMatcher": ["$gcc"]
- },
- {
- "label": "Clean Driver AGL",
- "type": "shell",
- "command": "./scripts/make-agl.sh 6864c558 clean",
- "group": "build",
- "problemMatcher": ["$gcc"]
- },
- {
- "label": "Deploy Driver AGL",
- "type": "shell",
- "command": "rsync -av snd-avirt-core.ko dummy/snd-avirt-ap-dummy.ko loopback/snd-avirt-ap-loopback.ko root@192.168.1.193:/lib/modules/4.14.0-yocto-standard/extra",
- "problemMatcher": ["$gcc"]
- }
- ]
-}
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "type": "shell",
+ "presentation": {
+ "reveal": "always"
+ },
+ "tasks": [
+ {
+ "label": "Make Drivers",
+ "command": "make all",
+ "group": "build",
+ "problemMatcher": [
+ "$gcc"
+ ]
+ },
+ {
+ "label": "Clean Drivers",
+ "command": "make clean",
+ "group": "build",
+ "problemMatcher": [
+ "$gcc"
+ ]
+ },
+ {
+ "label": "Unload Drivers",
+ "command": "sudo sh ./scripts/unload.sh",
+ "problemMatcher": []
+ },
+ {
+ "label": "Load Drivers",
+ "command": "sudo sh ./scripts/load.sh",
+ "problemMatcher": []
+ },
+ {
+ "label": "Make Driver AGL (XDS)",
+ "type": "shell",
+ "command": "./scripts/make-agl-xds.sh 6864c558",
+ "group": "build",
+ "problemMatcher": [
+ "$gcc"
+ ]
+ },
+ {
+ "label": "Clean Driver AGL (XDS)",
+ "type": "shell",
+ "command": "./scripts/make-agl-xds.sh 6864c558 clean",
+ "group": "build",
+ "problemMatcher": [
+ "$gcc"
+ ]
+ },
+ {
+ "label": "Make Driver AGL",
+ "type": "shell",
+ "command": "./scripts/make-agl.sh",
+ "group": "build",
+ "problemMatcher": [
+ "$gcc"
+ ]
+ },
+ {
+ "label": "Clean Driver AGL",
+ "type": "shell",
+ "command": "./scripts/make-agl.sh clean",
+ "group": "build",
+ "problemMatcher": [
+ "$gcc"
+ ]
+ },
+ {
+ "label": "Deploy Driver AGL",
+ "type": "shell",
+ "command": "scp snd-avirt-core.ko dummy/snd-avirt-ap-dummy.ko loopback/snd-avirt-ap-loopback.ko root@192.168.1.193:/lib/modules/4.14.35-yocto-standard/extra",
+ "problemMatcher": [
+ "$gcc"
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/Makefile b/Makefile
index 06a7c0c..377e8ec 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_AVIRT) += snd-avirt-core.o
snd-avirt-core-y := core.o
snd-avirt-core-y += pcm.o
snd-avirt-core-y += configfs.o
+snd-avirt-core-y += sysfs.o
ifeq ($(CONFIG_AVIRT_BUILDLOCAL),)
CCFLAGS_AVIRT := "drivers/staging/"
@@ -21,6 +22,7 @@ obj-$(CONFIG_AVIRT_AP_LOOPBACK) += loopback/
###
ifdef PKG_CONFIG_SYSROOT_DIR
# AGL SDK kernel src path
+ LDFLAGS= # Need to set, since unrecognized option '-Wl,-01'
KERNEL_SRC=$(PKG_CONFIG_SYSROOT_DIR)/usr/src/kernel
else
# General Linux distro kernel src path
diff --git a/configfs.c b/configfs.c
index 12e95a5..5668cb3 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)
@@ -47,70 +85,147 @@ static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item,
snd_avirt_stream_from_config_item(item);
split = strsep((char **)&page, "\n");
- memcpy(stream->map, (char *)split, count);
+ snd_avirt_stream_set_map(stream, split);
return count;
}
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)
- 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,19 +337,31 @@ 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,
};
static struct configfs_subsystem cfg_subsys = {
- .su_group = {
- .cg_item = {
+ .su_group =
+ {
+ .cg_item =
+ {
.ci_namebuf = "snd-avirt",
.ci_type = &cfg_avirt_group_type,
},
@@ -212,6 +379,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 +389,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 6fd3ab3..2c643fc 100644
--- a/core.c
+++ b/core.c
@@ -40,220 +40,36 @@ static struct snd_avirt_core core = {
static LIST_HEAD(audiopath_list);
-struct snd_avirt_audiopath_obj {
- struct kobject kobj;
- struct list_head list;
- struct snd_avirt_audiopath *path;
-};
-
-static struct kset *snd_avirt_audiopath_kset;
-static struct kobject *kobj;
-
-#define to_audiopath_obj(d) \
- container_of(d, struct snd_avirt_audiopath_obj, kobj)
-#define to_audiopath_attr(a) \
- container_of(a, struct snd_avirt_audiopath_attribute, attr)
-
-/**
- * struct snd_avirt_audiopath_attribute - access the attributes of Audio Path
- * @attr: attributes of an Audio Path
- * @show: pointer to the show function
- * @store: pointer to the store function
- */
-struct snd_avirt_audiopath_attribute {
- struct attribute attr;
- ssize_t (*show)(struct snd_avirt_audiopath_obj *d,
- struct snd_avirt_audiopath_attribute *attr, char *buf);
- ssize_t (*store)(struct snd_avirt_audiopath_obj *d,
- struct snd_avirt_audiopath_attribute *attr,
- const char *buf, size_t count);
-};
-
-/**
- * audiopath_attr_show - show function of an Audio Path
- * @kobj: pointer to kobject
- * @attr: pointer to attribute struct
- * @buf: buffer
- */
-static ssize_t audiopath_attr_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- struct snd_avirt_audiopath_attribute *audiopath_attr;
- struct snd_avirt_audiopath_obj *audiopath_obj;
-
- audiopath_attr = to_audiopath_attr(attr);
- audiopath_obj = to_audiopath_obj(kobj);
-
- if (!audiopath_attr->show)
- return -EIO;
-
- return audiopath_attr->show(audiopath_obj, audiopath_attr, buf);
-}
-
/**
- * audiopath_attr_store - attribute store function of Audio Path object
- * @kobj: pointer to kobject
- * @attr: pointer to attribute struct
- * @buf: buffer
- * @len: length of buffer
+ * pcm_private_free - callback function to free private data allocated to pcm
+ * @pcm: the PCM object
*/
-static ssize_t audiopath_attr_store(struct kobject *kobj,
- struct attribute *attr, const char *buf,
- size_t len)
-{
- struct snd_avirt_audiopath_attribute *audiopath_attr;
- struct snd_avirt_audiopath_obj *audiopath_obj;
-
- audiopath_attr = to_audiopath_attr(attr);
- audiopath_obj = to_audiopath_obj(kobj);
-
- if (!audiopath_attr->store)
- return -EIO;
- return audiopath_attr->store(audiopath_obj, audiopath_attr, buf, len);
-}
-
-static const struct sysfs_ops snd_avirt_audiopath_sysfs_ops = {
- .show = audiopath_attr_show,
- .store = audiopath_attr_store,
-};
-
-/**
- * snd_avirt_audiopath_release - Audio Path release function
- * @kobj: pointer to Audio Paths's kobject
- */
-static void snd_avirt_audiopath_release(struct kobject *kobj)
-{
- struct snd_avirt_audiopath_obj *audiopath_obj = to_audiopath_obj(kobj);
-
- kfree(audiopath_obj);
-}
-
-static ssize_t
- audiopath_name_show(struct snd_avirt_audiopath_obj *audiopath_obj,
- struct snd_avirt_audiopath_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%s\n", audiopath_obj->path->name);
-}
-
-static ssize_t
- audiopath_version_show(struct snd_avirt_audiopath_obj *audiopath_obj,
- struct snd_avirt_audiopath_attribute *attr,
- char *buf)
-{
- struct snd_avirt_audiopath *audiopath = audiopath_obj->path;
-
- return sprintf(buf, "%d.%d.%d\n", audiopath->version[0],
- audiopath->version[1], audiopath->version[2]);
-}
-
-static struct snd_avirt_audiopath_attribute snd_avirt_audiopath_attrs[] = {
- __ATTR_RO(audiopath_name),
- __ATTR_RO(audiopath_version),
-};
-
-static struct attribute *snd_avirt_audiopath_def_attrs[] = {
- &snd_avirt_audiopath_attrs[0].attr,
- &snd_avirt_audiopath_attrs[1].attr,
- NULL,
-};
-
-static struct kobj_type snd_avirt_audiopath_ktype = {
- .sysfs_ops = &snd_avirt_audiopath_sysfs_ops,
- .release = snd_avirt_audiopath_release,
- .default_attrs = snd_avirt_audiopath_def_attrs,
-};
-
-/**
- * create_snd_avirt_audiopath_obj - creates an Audio Path object
- * @uid: Unique ID of the Audio Path
- *
- * This creates an Audio Path object and assigns the kset and registers
- * it with sysfs.
- * @return: Pointer to the Audio Path object or NULL if it failed.
- */
-static struct snd_avirt_audiopath_obj *
- create_snd_avirt_audiopath_obj(const char *uid)
-{
- struct snd_avirt_audiopath_obj *snd_avirt_audiopath;
- int retval;
-
- snd_avirt_audiopath = kzalloc(sizeof(*snd_avirt_audiopath), GFP_KERNEL);
- if (!snd_avirt_audiopath)
- return NULL;
- snd_avirt_audiopath->kobj.kset = snd_avirt_audiopath_kset;
- retval = kobject_init_and_add(&snd_avirt_audiopath->kobj,
- &snd_avirt_audiopath_ktype, kobj, "%s",
- uid);
- if (retval) {
- kobject_put(&snd_avirt_audiopath->kobj);
- return NULL;
- }
- kobject_uevent(&snd_avirt_audiopath->kobj, KOBJ_ADD);
- return snd_avirt_audiopath;
-}
-
-/**
- * destroy_snd_avirt_audiopath_obj - destroys an Audio Path object
- * @name: the Audio Path object
- */
-static void destroy_snd_avirt_audiopath_obj(struct snd_avirt_audiopath_obj *p)
-{
- kobject_put(&p->kobj);
-}
-
-/**
- * pcm_private_data_free - callback function to free private data allocated to pcm
- * @pcm: the PCM with private data
- */
-static void pcm_private_data_free(struct snd_pcm *pcm)
+static void pcm_private_free(struct snd_pcm *pcm)
{
struct snd_avirt_private_data *avirt_private_data;
- D_PRINTK("Issuing free to private data struct");
- if (pcm->private_data) {
- avirt_private_data = pcm->private_data;
- if (avirt_private_data->ap_private_data)
+ /* 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 &&
+ avirt_private_data->ap_private_free)
avirt_private_data->ap_private_free(pcm);
}
kfree(pcm->private_data);
}
-static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream)
+static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream)
{
struct snd_avirt_private_data *avirt_private_data;
- struct snd_avirt_audiopath *audiopath;
- struct snd_pcm_ops *pcm_ops, *pcm_ops_ap;
struct snd_pcm *pcm;
bool playback = false, capture = false;
int err;
- audiopath = snd_avirt_audiopath_get(stream->map);
- if (!audiopath) {
- D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map);
- return ERR_PTR(-EFAULT);
- }
-
- if (!stream->direction) {
- pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops;
+ if (!stream->direction)
playback = true;
- } else {
- pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_capture_ops;
+ else
capture = true;
- }
- pcm_ops = &pcm_ops_avirt;
-
- /* Set PCM ops for the Audio Path*/
- PCM_OPS_SET(pcm_ops_ap, &pcm_ops, close);
- PCM_OPS_SET(pcm_ops_ap, &pcm_ops, prepare);
- PCM_OPS_SET(pcm_ops_ap, &pcm_ops, trigger);
- PCM_OPS_SET(pcm_ops_ap, &pcm_ops, pointer);
- PCM_OPS_SET(pcm_ops_ap, &pcm_ops, get_time_info);
- PCM_OPS_SET(pcm_ops_ap, &pcm_ops, silence);
- PCM_OPS_SET(pcm_ops_ap, &pcm_ops, copy);
- PCM_OPS_SET(pcm_ops_ap, &pcm_ops, mmap);
- PCM_OPS_SET(pcm_ops_ap, &pcm_ops, ack);
/** Special case: loopback */
if (!strcmp(stream->map, "ap_loopback")) {
@@ -264,14 +80,18 @@ static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream)
err = snd_pcm_new(core.card, stream->name, stream->device, playback,
capture, &pcm);
- if (err < 0)
+ if (err < 0) {
+ D_ERRORK("Failed to create PCM device for stream: '%s'",
+ stream->name);
return ERR_PTR(err);
+ }
/** Register driver callbacks */
if (playback)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ stream->pcm_ops);
if (capture)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, stream->pcm_ops);
pcm->info_flags = 0;
strcpy(pcm->name, stream->name);
@@ -279,11 +99,66 @@ static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream)
avirt_private_data = kzalloc(sizeof(*avirt_private_data), GFP_KERNEL);
pcm->private_data = avirt_private_data;
// Set the private free function for the private user data
- pcm->private_free = pcm_private_data_free;
+ pcm->private_free = pcm_private_free;
+
+ snd_device_register(core.card, pcm);
return pcm;
}
+int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map)
+{
+ struct snd_avirt_audiopath *audiopath;
+ 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);
+ return -EPERM;
+ }
+
+ if (!strcmp(stream->map, map))
+ return -1;
+
+ memcpy(stream->map, (char *)map, strlen(map));
+ audiopath = snd_avirt_audiopath_get(map);
+ if (!audiopath) {
+ D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map);
+ return -EFAULT;
+ }
+
+ if (!stream->direction)
+ pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops;
+ else
+ pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_capture_ops;
+
+ if (!pcm_ops_ap) {
+ D_ERRORK("No PCM ops for direction '%s' for Audio Path: %s",
+ (stream->direction) ? "capture" : "playback",
+ stream->map);
+ return -EFAULT;
+ }
+
+ /* 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);
+ PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, copy);
+ PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, mmap);
+ PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, ack);
+
+ /* If not created, create the PCM device now */
+ if (!stream->pcm) {
+ stream->pcm = snd_avirt_pcm_create(stream);
+ if (IS_ERR_OR_NULL(stream->pcm))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
static int snd_avirt_streams_get(const char *map,
struct snd_avirt_stream_array *stream_array)
{
@@ -311,14 +186,14 @@ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid)
{
struct snd_avirt_audiopath_obj *ap_obj;
- list_for_each_entry(ap_obj, &audiopath_list, list) {
- // pr_info("get_ap %s\n", ap_obj->path->uid);
+ list_for_each_entry (ap_obj, &audiopath_list, list) {
if (!strcmp(ap_obj->path->uid, uid))
return ap_obj->path;
}
return NULL;
}
+EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get);
/**
* snd_avirt_audiopath_register - register Audio Path with AVIRT
@@ -335,7 +210,7 @@ int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath)
return -EINVAL;
}
- audiopath_obj = create_snd_avirt_audiopath_obj(audiopath->uid);
+ audiopath_obj = snd_avirt_audiopath_create_obj(audiopath->uid);
if (!audiopath_obj) {
D_INFOK("Failed to alloc driver object");
return -ENOMEM;
@@ -380,7 +255,7 @@ int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath)
}
list_del(&audiopath_obj->list);
- destroy_snd_avirt_audiopath_obj(audiopath_obj);
+ snd_avirt_audiopath_destroy_obj(audiopath_obj);
D_INFOK("Deregistered Audio Path %s", audiopath->uid);
return 0;
@@ -388,6 +263,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
@@ -414,7 +311,16 @@ struct snd_avirt_stream *snd_avirt_stream_create(const char *name,
stream->direction = direction;
stream->device = core.stream_count++;
- D_INFOK("name: %s device:%d", name, stream->device);
+ /* Initialize PCM ops table for this stream.
+ * Will be populated once map is known */
+ stream->pcm_ops = kzalloc(sizeof(struct snd_pcm_ops), GFP_KERNEL);
+ if (!stream->pcm_ops) {
+ D_ERRORK("Failed to allocate PCM ops table");
+ return ERR_PTR(-EFAULT);
+ }
+ memcpy(stream->pcm_ops, &pcm_ops_avirt, sizeof(struct snd_pcm_ops));
+
+ D_INFOK("name:%s device:%d", name, stream->device);
return stream;
}
@@ -423,39 +329,25 @@ int snd_avirt_streams_seal(void)
{
int err = 0, i = 0;
struct snd_avirt_audiopath_obj *ap_obj;
- struct snd_avirt_stream *stream;
struct snd_avirt_stream_array stream_array;
- struct config_item *item;
- struct list_head *entry;
if (core.streams_sealed) {
D_ERRORK("streams are already sealed!");
return -1;
}
- list_for_each(entry, &core.stream_group->cg_children) {
- item = container_of(entry, struct config_item, ci_entry);
- stream = snd_avirt_stream_from_config_item(item);
- if (!stream)
- return -EFAULT;
- stream->pcm = pcm_create(stream);
- if (IS_ERR_OR_NULL(stream->pcm))
- return (PTR_ERR(stream->pcm));
- }
-
- list_for_each_entry(ap_obj, &audiopath_list, list) {
- D_INFOK("configure() AP uid: %s", ap_obj->path->uid);
+ list_for_each_entry (ap_obj, &audiopath_list, list) {
for (i = 0; i < MAX_STREAMS; i++)
stream_array.streams[i] = NULL;
stream_array.count = 0;
- if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) > 0)
- ap_obj->path->configure(core.card, &stream_array);
- }
-
- err = snd_card_register(core.card);
- if (err < 0) {
- D_ERRORK("Sound card registration failed!");
- snd_card_free(core.card);
+ if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) <=
+ 0)
+ continue;
+
+ D_INFOK("Do configure for AP: %s streams:%d cb:%p",
+ ap_obj->path->uid, stream_array.count,
+ ap_obj->path->configure);
+ ap_obj->path->configure(core.card, &stream_array);
}
core.streams_sealed = true;
@@ -479,7 +371,7 @@ struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device)
return ERR_PTR(-EINVAL);
}
- list_for_each(entry, &core.stream_group->cg_children) {
+ list_for_each (entry, &core.stream_group->cg_children) {
item = container_of(entry, struct config_item, ci_entry);
stream = snd_avirt_stream_from_config_item(item);
if (!stream)
@@ -554,27 +446,30 @@ static int __init snd_avirt_core_init(void)
return PTR_ERR(core.class);
}
- core.dev = device_create(core.class, NULL, 0, NULL, "avirtcore");
+ core.dev = device_create(core.class, NULL, 0, NULL, "core");
if (IS_ERR(core.dev)) {
err = PTR_ERR(core.dev);
goto exit_class;
}
- snd_avirt_audiopath_kset =
- kset_create_and_add("audiopaths", NULL, &core.dev->kobj);
- if (!snd_avirt_audiopath_kset) {
- err = -ENOMEM;
+ err = snd_avirt_sysfs_init(&core);
+ if (err < 0)
goto exit_class_container;
+
+ err = snd_card_register(core.card);
+ if (err < 0) {
+ D_ERRORK("Sound card registration failed!");
+ snd_card_free(core.card);
}
err = snd_avirt_configfs_init(&core);
if (err < 0)
- goto exit_kset;
+ goto exit_sysfs;
return 0;
-exit_kset:
- kset_unregister(snd_avirt_audiopath_kset);
+exit_sysfs:
+ snd_avirt_sysfs_exit(&core);
exit_class_container:
device_destroy(core.class, 0);
exit_class:
@@ -592,7 +487,7 @@ exit_platform_device:
static void __exit snd_avirt_core_exit(void)
{
snd_avirt_configfs_exit(&core);
- kset_unregister(snd_avirt_audiopath_kset);
+ snd_avirt_sysfs_exit(&core);
device_destroy(core.class, 0);
class_destroy(core.class);
platform_device_unregister(core.plat_dev);
diff --git a/core.h b/core.h
index 40252e6..e4fb7a4 100644
--- a/core.h
+++ b/core.h
@@ -23,10 +23,17 @@ 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;
};
+struct snd_avirt_audiopath_obj {
+ struct kobject kobj;
+ struct list_head list;
+ struct snd_avirt_audiopath *path;
+};
+
/**
* snd_avirt_configfs_init - Initialise the configfs system
* @core: The snd_avirt_core pointer
@@ -41,6 +48,34 @@ int __init snd_avirt_configfs_init(struct snd_avirt_core *core);
void __exit snd_avirt_configfs_exit(struct snd_avirt_core *core);
/**
+ * snd_avirt_sysfs_init - Initialise the sysfs system
+ * @core: The snd_avirt_core pointer
+ * @return: 0 on success, negative ERRNO on failure
+ */
+int __init snd_avirt_sysfs_init(struct snd_avirt_core *core);
+
+/**
+ * snd_avirt_sysfs_exit - Clean up and exit the sysfs system
+ * @core: The snd_avirt_core pointer
+ */
+void __exit snd_avirt_sysfs_exit(struct snd_avirt_core *core);
+
+/**
+ * snd_avirt_audiopath_obj - creates an Audio Path object, assigns the kset
+ * and registers it with sysfs.
+ * @uid: Unique ID of the Audio Path
+ * @return: Pointer to the Audio Path object or NULL if it failed.
+ */
+struct snd_avirt_audiopath_obj *snd_avirt_audiopath_create_obj(const char *uid);
+
+/**
+ * snd_avirt_audiopath_destroy_obj - destroys an Audio Path object, deregisters
+ * it with sysfs
+ * @name: the Audio Path object
+ */
+void snd_avirt_audiopath_destroy_obj(struct snd_avirt_audiopath_obj *p);
+
+/**
* snd_avirt_streams_seal - Register the sound card to user space
* @return: 0 on success, negative ERRNO on failure
*/
@@ -68,6 +103,13 @@ struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device);
*/
struct snd_avirt_stream *snd_avirt_stream_create(const char *name,
int direction);
+/**
+ * snd_avirt_stream_set_map - Set Audio Path mapping for a given stream
+ * @stream: The stream to assign the mapping to.
+ * @map: The Audio Path UID to map the stream to.
+ * @return: 0 on success, negative ERRNO on failure
+ */
+int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map);
/**
* snd_avirt_audiopath_get - retrieves the Audio Path by it's UID
@@ -77,6 +119,15 @@ struct snd_avirt_stream *snd_avirt_stream_create(const char *name,
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
@@ -87,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/loopback/loopback.c b/loopback/loopback.c
index 4181392..3463989 100644
--- a/loopback/loopback.c
+++ b/loopback/loopback.c
@@ -238,8 +238,6 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
struct loopback_cable *cable = dpcm->cable;
int err, stream = 1 << substream->stream;
- AP_INFOK();
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
err = loopback_check_format(cable, substream->stream);
diff --git a/pcm.c b/pcm.c
index 411db40..e91b121 100644
--- a/pcm.c
+++ b/pcm.c
@@ -67,6 +67,11 @@ 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");
+ return -EPERM;
+ }
+
// Find the Audio Path mapped to this device
stream = snd_avirt_stream_find_by_device(substream->pcm->device);
if (IS_ERR_VALUE(stream) || !stream)
@@ -84,6 +89,10 @@ static int pcm_open(struct snd_pcm_substream *substream)
// Setup remaining hw properties
chans = stream->channels;
+ if (chans == 0) {
+ D_ERRORK("Channels cannot be 0");
+ return -EINVAL;
+ }
hw->channels_min = chans;
hw->channels_max = chans;
@@ -92,6 +101,22 @@ static int pcm_open(struct snd_pcm_substream *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(
+ (struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
+ close, 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
@@ -105,7 +130,6 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
int retval;
- size_t bufsz;
/* Check supported channels */
if ((params_channels(hw_params) <
@@ -117,10 +141,8 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- bufsz = params_buffer_bytes(hw_params) *
- substream->runtime->hw.periods_max;
-
- retval = snd_pcm_lib_alloc_vmalloc_buffer(substream, bufsz);
+ retval = snd_pcm_lib_alloc_vmalloc_buffer(
+ substream, params_buffer_bytes(hw_params));
if (retval < 0)
D_ERRORK("pcm: buffer allocation failed: %d", retval);
@@ -144,6 +166,8 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
err = DO_AUDIOPATH_CB(
(struct snd_avirt_audiopath *)PRIVATE_DATA(substream)->audiopath,
hw_free, substream);
+ if (err < 0)
+ return err;
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
@@ -154,6 +178,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
*/
struct snd_pcm_ops pcm_ops_avirt = {
.open = pcm_open,
+ .close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_hw_params,
.hw_free = pcm_hw_free,
diff --git a/scripts/load.sh b/scripts/load.sh
index 103a63b..e3f21b4 100755
--- a/scripts/load.sh
+++ b/scripts/load.sh
@@ -8,6 +8,6 @@ insmod dummy/snd-avirt-ap-dummy.ko
insmod loopback/snd-avirt-ap-loopback.ko
# Run the test script
-./scripts/test_configfs.sh
+./scripts/test_configfs_run.sh
echo "Drivers Loaded!"
diff --git a/scripts/make-agl-xds.sh b/scripts/make-agl-xds.sh
new file mode 100755
index 0000000..5cb8048
--- /dev/null
+++ b/scripts/make-agl-xds.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Get SDK path
+sdk_id=$1 # first arg must be XDS_SDK_ID
+shift 1
+long_sdkpath=$(xds-cli sdks get $sdk_id | grep Path)
+sdkpath=${long_sdkpath:4}
+
+# Build
+/opt/AGL/bin/xds-cli exec --config xds-project.conf -- \
+ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y \
+ CONFIG_AVIRT_AP_DUMMY=m \
+ CONFIG_AVIRT_AP_LOOPBACK=m \
+ make -C $sdkpath/sysroots/aarch64-agl-linux/usr/src/kernel M=$(pwd) $@
diff --git a/scripts/make-agl.sh b/scripts/make-agl.sh
index 6c94fbb..f4a6235 100755
--- a/scripts/make-agl.sh
+++ b/scripts/make-agl.sh
@@ -1,14 +1,16 @@
#!/bin/bash
-# Get SDK path
-sdk_id=$1 # first arg must be XDS_SDK_ID
-shift 1
-long_sdkpath=$(xds-cli sdks get $sdk_id | grep Path)
-sdkpath=${long_sdkpath:4}
+SDK_ROOT=/opt/agl-sdk
+SDK_VERSION=7.0.0
+SDK_VAR=aarch64
+
+# Source environment
+unset LD_LIBRARY_PATH
+source $SDK_ROOT/$SDK_VERSION-$SDK_VAR/environment-setup-aarch64-agl-linux
# Build
-/opt/AGL/bin/xds-cli exec --config xds-project.conf -- \
- LDFLAGS= CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y \
- CONFIG_AVIRT_AP_DUMMY=m \
- CONFIG_AVIRT_AP_LOOPBACK=m \
- make -C $sdkpath/sysroots/aarch64-agl-linux/usr/src/kernel M=$(pwd) $@
+LDFLAGS= \
+CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y \
+CONFIG_AVIRT_AP_DUMMY=m \
+CONFIG_AVIRT_AP_LOOPBACK=m \
+make -C $SDK_ROOT/$SDK_VERSION-$SDK_VAR/sysroots/$SDK_VAR-agl-linux/usr/src/kernel M=$(pwd) $@
diff --git a/scripts/test_configfs_cleanup.sh b/scripts/test_configfs_cleanup.sh
new file mode 100755
index 0000000..9697818
--- /dev/null
+++ b/scripts/test_configfs_cleanup.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+rmdir /config/snd-avirt/streams/playback_media
+rmdir /config/snd-avirt/streams/playback_navigation
+rmdir /config/snd-avirt/streams/playback_emergency
+rmdir /config/snd-avirt/streams/capture_voice
diff --git a/scripts/test_configfs.sh b/scripts/test_configfs_run.sh
index b6a56f6..b6a56f6 100755
--- a/scripts/test_configfs.sh
+++ b/scripts/test_configfs_run.sh
diff --git a/scripts/unload.sh b/scripts/unload.sh
index a18e1f5..acce310 100755
--- a/scripts/unload.sh
+++ b/scripts/unload.sh
@@ -1,7 +1,7 @@
#!/bin/sh
-rmdir /config/snd-avirt/streams/playback_*
-rmdir /config/snd-avirt/streams/capture_*
+# Cleanup the configfs filesystem
+./scripts/test_configfs_cleanup.sh
rm_module() {
lsmod |grep "^$1\>" && rmmod $1 || true
diff --git a/sound/avirt.h b/sound/avirt.h
index 0ebac86..138d0ca 100644
--- a/sound/avirt.h
+++ b/sound/avirt.h
@@ -3,7 +3,7 @@
* AVIRT - ALSA Virtual Soundcard
*
* Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd
- *
+ *
* avirt.h - AVIRT system-level header
*/
@@ -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;
};
/**
@@ -61,25 +80,31 @@ struct snd_avirt_stream {
unsigned int device; /* Stream PCM device no. */
unsigned int direction; /* Stream direction */
struct snd_pcm *pcm; /* ALSA PCM */
+ struct snd_pcm_ops *pcm_ops; /* ALSA PCM ops */
struct config_item item; /* configfs item reference */
};
/**
+ * Audio stream group
+ */
+struct snd_avirt_stream_array {
+ struct snd_avirt_stream *streams[MAX_STREAMS];
+ int count;
+};
+
+/**
+ * snd_avirt_private_free - free Audio Path private data from function prototype
+ * @pcm: The PCM object
+ */
+typedef void (*snd_avirt_ap_private_free)(struct snd_pcm *pcm);
+/**
* Private Data Expansion
*/
struct snd_avirt_private_data {
struct snd_avirt_audiopath *audiopath;
void *ap_private_data;
- void (*ap_private_free)(struct snd_pcm *pcm);
-};
-
-/**
- * Audio stream group
- */
-struct snd_avirt_stream_array {
- struct snd_avirt_stream *streams[MAX_STREAMS];
- int count;
+ snd_avirt_ap_private_free ap_private_free;
};
/**
@@ -97,6 +122,13 @@ int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath);
int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath);
/**
+ * 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);
+
+/**
* snd_avirt_pcm_period_elapsed - PCM buffer complete callback
* @substream: pointer to ALSA PCM substream
*
diff --git a/sysfs.c b/sysfs.c
new file mode 100644
index 0000000..29f9b5d
--- /dev/null
+++ b/sysfs.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AVIRT - ALSA Virtual Soundcard
+ *
+ * Copyright (c) 2010-2019 Fiberdyne Systems Pty Ltd
+ *
+ * configfs.c - AVIRT sysfs support
+ */
+
+#include <linux/slab.h>
+
+#include "core.h"
+
+#define D_LOGNAME "sysfs"
+
+#define D_INFOK(fmt, args...) DINFO(D_LOGNAME, fmt, ##args)
+#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args)
+#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args)
+
+static struct kset *snd_avirt_audiopath_kset;
+static struct kobject *kobj;
+
+#define to_audiopath_obj(d) \
+ container_of(d, struct snd_avirt_audiopath_obj, kobj)
+#define to_audiopath_attr(a) \
+ container_of(a, struct snd_avirt_audiopath_attribute, attr)
+
+/**
+ * struct snd_avirt_audiopath_attribute - access the attributes of Audio Path
+ * @attr: attributes of an Audio Path
+ * @show: pointer to the show function
+ * @store: pointer to the store function
+ */
+struct snd_avirt_audiopath_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct snd_avirt_audiopath_obj *d,
+ struct snd_avirt_audiopath_attribute *attr, char *buf);
+ ssize_t (*store)(struct snd_avirt_audiopath_obj *d,
+ struct snd_avirt_audiopath_attribute *attr,
+ const char *buf, size_t count);
+};
+
+/**
+ * audiopath_attr_show - show function of an Audio Path
+ * @kobj: pointer to kobject
+ * @attr: pointer to attribute struct
+ * @buf: buffer
+ */
+static ssize_t audiopath_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct snd_avirt_audiopath_attribute *audiopath_attr;
+ struct snd_avirt_audiopath_obj *audiopath_obj;
+
+ audiopath_attr = to_audiopath_attr(attr);
+ audiopath_obj = to_audiopath_obj(kobj);
+
+ if (!audiopath_attr->show)
+ return -EIO;
+
+ return audiopath_attr->show(audiopath_obj, audiopath_attr, buf);
+}
+
+/**
+ * audiopath_attr_store - attribute store function of Audio Path object
+ * @kobj: pointer to kobject
+ * @attr: pointer to attribute struct
+ * @buf: buffer
+ * @len: length of buffer
+ */
+static ssize_t audiopath_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf,
+ size_t len)
+{
+ struct snd_avirt_audiopath_attribute *audiopath_attr;
+ struct snd_avirt_audiopath_obj *audiopath_obj;
+
+ audiopath_attr = to_audiopath_attr(attr);
+ audiopath_obj = to_audiopath_obj(kobj);
+
+ if (!audiopath_attr->store)
+ return -EIO;
+ return audiopath_attr->store(audiopath_obj, audiopath_attr, buf, len);
+}
+
+static const struct sysfs_ops snd_avirt_audiopath_sysfs_ops = {
+ .show = audiopath_attr_show,
+ .store = audiopath_attr_store,
+};
+
+/**
+ * snd_avirt_audiopath_release - Audio Path release function
+ * @kobj: pointer to Audio Paths's kobject
+ */
+static void snd_avirt_audiopath_release(struct kobject *kobj)
+{
+ struct snd_avirt_audiopath_obj *audiopath_obj = to_audiopath_obj(kobj);
+
+ kfree(audiopath_obj);
+}
+
+static ssize_t name_show(struct snd_avirt_audiopath_obj *audiopath_obj,
+ struct snd_avirt_audiopath_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", audiopath_obj->path->name);
+}
+
+static ssize_t version_show(struct snd_avirt_audiopath_obj *audiopath_obj,
+ struct snd_avirt_audiopath_attribute *attr,
+ char *buf)
+{
+ struct snd_avirt_audiopath *audiopath = audiopath_obj->path;
+
+ return sprintf(buf, "%d.%d.%d\n", audiopath->version[0],
+ 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,
+};
+
+static struct kobj_type snd_avirt_audiopath_ktype = {
+ .sysfs_ops = &snd_avirt_audiopath_sysfs_ops,
+ .release = snd_avirt_audiopath_release,
+ .default_attrs = snd_avirt_audiopath_def_attrs,
+};
+
+struct snd_avirt_audiopath_obj *snd_avirt_audiopath_create_obj(const char *uid)
+{
+ struct snd_avirt_audiopath_obj *snd_avirt_audiopath;
+ int retval;
+
+ snd_avirt_audiopath = kzalloc(sizeof(*snd_avirt_audiopath), GFP_KERNEL);
+ if (!snd_avirt_audiopath)
+ return NULL;
+ snd_avirt_audiopath->kobj.kset = snd_avirt_audiopath_kset;
+ retval = kobject_init_and_add(&snd_avirt_audiopath->kobj,
+ &snd_avirt_audiopath_ktype, kobj, "%s",
+ uid);
+ if (retval) {
+ kobject_put(&snd_avirt_audiopath->kobj);
+ return NULL;
+ }
+ kobject_uevent(&snd_avirt_audiopath->kobj, KOBJ_ADD);
+ return snd_avirt_audiopath;
+}
+
+void snd_avirt_audiopath_destroy_obj(struct snd_avirt_audiopath_obj *p)
+{
+ kobject_put(&p->kobj);
+}
+
+int __init snd_avirt_sysfs_init(struct snd_avirt_core *core)
+{
+ snd_avirt_audiopath_kset =
+ kset_create_and_add("audiopaths", NULL, &core->dev->kobj);
+ if (!snd_avirt_audiopath_kset)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void __exit snd_avirt_sysfs_exit(struct snd_avirt_core *core)
+{
+ kset_unregister(snd_avirt_audiopath_kset);
+}