diff options
Diffstat (limited to 'configfs.c')
-rw-r--r-- | configfs.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/configfs.c b/configfs.c new file mode 100644 index 0000000..dd7b7df --- /dev/null +++ b/configfs.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA Virtual Soundcard + * + * configfs.c - configfs for virtual ALSA card + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#include <sound/core.h> +#include "core_internal.h" + +static bool streams_sealed = false; + +static ssize_t cfg_avirt_stream_direction_show(struct config_item *item, + char *page) +{ + ssize_t count; + struct avirt_stream *stream = avirt_stream_from_config_item(item); + + count = sprintf(page, "%d\n", stream->direction); + + return count; +} +CONFIGFS_ATTR_RO(cfg_avirt_stream_, direction); + +static ssize_t cfg_avirt_stream_map_show(struct config_item *item, char *page) +{ + struct avirt_stream *stream = avirt_stream_from_config_item(item); + + return sprintf(page, "%s\n", stream->map); +} + +static ssize_t cfg_avirt_stream_map_store(struct config_item *item, + const char *page, size_t count) +{ + struct avirt_stream *stream = avirt_stream_from_config_item(item); + + memcpy(stream->map, (char *)page, count); + + return count; +} +CONFIGFS_ATTR(cfg_avirt_stream_, map); + +static ssize_t cfg_avirt_stream_channels_show(struct config_item *item, + char *page) +{ + ssize_t count; + struct avirt_stream *stream = avirt_stream_from_config_item(item); + + count = sprintf(page, "%d\n", stream->channels); + + return count; +} + +static ssize_t cfg_avirt_stream_channels_store(struct config_item *item, + const char *page, size_t count) +{ + int err; + struct avirt_stream *stream = avirt_stream_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) + return -ERANGE; + + stream->channels = tmp; + + pr_info("%s [ARGS] channels: %d\n", __func__, stream->channels); + + return count; +} +CONFIGFS_ATTR(cfg_avirt_stream_, channels); + +static struct configfs_attribute *cfg_avirt_stream_attrs[] = { + &cfg_avirt_stream_attr_channels, + &cfg_avirt_stream_attr_map, + &cfg_avirt_stream_attr_direction, + NULL, +}; + +static void cfg_avirt_stream_release(struct config_item *item) +{ + pr_info("%s [ARGS] item->name:%s\n", __func__, item->ci_namebuf); + kfree(avirt_stream_from_config_item(item)); +} + +static struct configfs_item_operations cfg_avirt_stream_ops = { + .release = cfg_avirt_stream_release, +}; + +static struct config_item_type cfg_avirt_stream_type = { + .ct_item_ops = &cfg_avirt_stream_ops, + .ct_attrs = cfg_avirt_stream_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item * +cfg_avirt_stream_make_item(struct config_group *group, const char *name) +{ + char *split; + int direction; + struct avirt_stream *stream; + + // Get prefix (playback_ or capture_) + split = strsep((char **)&name, "_"); + if (!split) { + pr_err("Stream name: '%s' invalid!\n", split); + pr_err("Must begin with playback_ * or capture_ *\n"); + return ERR_PTR(-EINVAL); + } + if (!strcmp(split, "playback")) { + direction = SNDRV_PCM_STREAM_PLAYBACK; + } else if (!strcmp(split, "capture")) { + direction = SNDRV_PCM_STREAM_CAPTURE; + } else { + pr_err("Stream name: '%s' invalid!\n", split); + pr_err("Must begin with playback_ * or capture_ *\n"); + return ERR_PTR(-EINVAL); + } + + // Get stream name, and create PCM for stream + split = strsep((char **)&name, "\n"); + stream = __avirt_stream_create(split, direction); + if (IS_ERR(stream)) + return ERR_PTR(PTR_ERR(stream)); + + config_item_init_type_name(&stream->item, name, &cfg_avirt_stream_type); + + return &stream->item; +} + +static ssize_t cfg_avirt_stream_group_sealed_show(struct config_item *item, + char *page) +{ + return snprintf(page, PAGE_SIZE, "%d\n", streams_sealed); +} + +static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, + const char *page, + size_t count) +{ + unsigned long tmp; + char *p = (char *)page; + + if (streams_sealed) { + pr_err("AVIRT streams are already sealed!\n"); + return -EPERM; + } + + CHK_ERR(kstrtoul(p, 10, &tmp)); + + if (tmp != 1) { + pr_err("AVIRT streams can only be sealed, not unsealed!\n"); + return -ERANGE; + } + + streams_sealed = (bool)tmp; + + CHK_ERR(__avirt_card_register()); + + return count; +} +CONFIGFS_ATTR(cfg_avirt_stream_group_, sealed); + +static struct configfs_attribute *cfg_avirt_stream_group_attrs[] = { + &cfg_avirt_stream_group_attr_sealed, + NULL, +}; + +static struct configfs_group_operations cfg_avirt_stream_group_ops = { + .make_item = cfg_avirt_stream_make_item}; + +static struct config_item_type cfg_stream_group_type = { + .ct_group_ops = &cfg_avirt_stream_group_ops, + .ct_attrs = cfg_avirt_stream_group_attrs, + .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 = + { + .ci_namebuf = "avirt", + .ci_type = &cfg_avirt_group_type, + }, + }, +}; + +int __init __avirt_configfs_init(struct avirt_core *core) +{ + int err; + + config_group_init(&cfg_subsys.su_group); + mutex_init(&cfg_subsys.su_mutex); + err = configfs_register_subsystem(&cfg_subsys); + if (err) { + pr_err("Cannot register configfs subsys!\n"); + return err; + } + core->stream_group = configfs_register_default_group( + &cfg_subsys.su_group, "streams", &cfg_stream_group_type); + if (IS_ERR(core->stream_group)) { + err = PTR_ERR(core->stream_group); + pr_err("Cannot register configfs default group 'streams'!\n"); + goto exit_configfs; + } + + return 0; + +exit_configfs: + configfs_unregister_subsystem(&cfg_subsys); + + return err; +} + +void __exit __avirt_configfs_exit(struct avirt_core *core) +{ + configfs_unregister_default_group(core->stream_group); + configfs_unregister_subsystem(&cfg_subsys); +} |