diff options
author | 2019-03-05 12:35:47 +1100 | |
---|---|---|
committer | 2019-03-05 12:36:02 +1100 | |
commit | 6a2f694005d8e31c74043b9b90c715bcdf394294 (patch) | |
tree | 9cd08c4772c2e13d5cff4e3ad49c6cce3f68be77 | |
parent | ea9a8ede7f343d589c8d9fac945f7b3d9dca9c3f (diff) | |
parent | 9f68614f664506535c873699dcfbca6e2515a24c (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.json | 13 | ||||
-rw-r--r-- | .vscode/tasks.json | 142 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | configfs.c | 262 | ||||
-rw-r--r-- | core.c | 369 | ||||
-rw-r--r-- | core.h | 62 | ||||
-rw-r--r-- | loopback/loopback.c | 2 | ||||
-rw-r--r-- | pcm.c | 35 | ||||
-rwxr-xr-x | scripts/load.sh | 2 | ||||
-rwxr-xr-x | scripts/make-agl-xds.sh | 14 | ||||
-rwxr-xr-x | scripts/make-agl.sh | 22 | ||||
-rwxr-xr-x | scripts/test_configfs_cleanup.sh | 6 | ||||
-rwxr-xr-x | scripts/test_configfs_run.sh (renamed from scripts/test_configfs.sh) | 0 | ||||
-rwxr-xr-x | scripts/unload.sh | 4 | ||||
-rw-r--r-- | sound/avirt.h | 54 | ||||
-rw-r--r-- | sysfs.c | 201 |
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 @@ -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 @@ -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: @@ -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); @@ -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); @@ -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 * @@ -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); +} |