diff options
author | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2018-10-29 17:43:12 +1100 |
---|---|---|
committer | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2018-10-29 17:45:19 +1100 |
commit | f55fa7657dd217a71af9198e0bc5754c0cd335ce (patch) | |
tree | a89858857e3e0ec7672866d086e12b3c58cf84e3 /configfs.c | |
parent | 96aa81e1426e64875850616e12c3415231ed14f8 (diff) | |
parent | 188712f58b309d94d26d7468a792326c464a7eda (diff) |
Merge remote-tracking branch 'agl/master' into flounder
Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
Diffstat (limited to 'configfs.c')
-rw-r--r-- | configfs.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/configfs.c b/configfs.c new file mode 100644 index 0000000..12e95a5 --- /dev/null +++ b/configfs.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AVIRT - ALSA Virtual Soundcard + * + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * configfs.c - AVIRT configfs support + */ + +#include <linux/slab.h> + +#include "core.h" + +#define D_LOGNAME "configfs" + +#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 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); + +static ssize_t cfg_snd_avirt_stream_map_show(struct config_item *item, + char *page) +{ + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); + + return sprintf(page, "%s\n", stream->map); +} + +static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item, + const char *page, size_t count) +{ + char *split; + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); + + split = strsep((char **)&page, "\n"); + memcpy(stream->map, (char *)split, count); + + return count; +} +CONFIGFS_ATTR(cfg_snd_avirt_stream_, map); + +static ssize_t cfg_snd_avirt_stream_channels_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->channels); + + return count; +} + +static ssize_t cfg_snd_avirt_stream_channels_store(struct config_item *item, + const char *page, + size_t count) +{ + int err; + struct snd_avirt_stream *stream = + snd_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; + + return count; +} +CONFIGFS_ATTR(cfg_snd_avirt_stream_, channels); + +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 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)); +} + +static struct configfs_item_operations cfg_snd_avirt_stream_ops = { + .release = cfg_snd_avirt_stream_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 * +cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name) +{ + char *split; + int direction; + struct snd_avirt_stream *stream; + + // Get prefix (playback_ or capture_) + split = strsep((char **)&name, "_"); + if (!split) { + D_ERRORK("Stream 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("Stream name: '%s' invalid!", split); + D_ERRORK("Must begin with playback_ * or capture_ "); + return ERR_PTR(-EINVAL); + } + + // Get stream name, and create PCM for stream + split = strsep((char **)&name, "\n"); + stream = snd_avirt_stream_create(split, direction); + if (IS_ERR(stream)) + return ERR_PTR(PTR_ERR(stream)); + + config_item_init_type_name(&stream->item, name, + &cfg_snd_avirt_stream_type); + + return &stream->item; +} + +static ssize_t cfg_snd_avirt_stream_group_sealed_show(struct config_item *item, + char *page) +{ + return snprintf(page, PAGE_SIZE, "%d\n", snd_avirt_streams_sealed()); +} + +static ssize_t cfg_snd_avirt_stream_group_sealed_store(struct config_item *item, + const char *page, + size_t count) +{ + unsigned long tmp; + char *p = (char *)page; + + CHK_ERR(kstrtoul(p, 10, &tmp)); + + if (tmp != 1) { + D_ERRORK("streams can only be sealed, not unsealed!"); + return -ERANGE; + } + + snd_avirt_streams_seal(); + + return count; +} +CONFIGFS_ATTR(cfg_snd_avirt_stream_group_, sealed); + +static struct configfs_attribute *cfg_snd_avirt_stream_group_attrs[] = { + &cfg_snd_avirt_stream_group_attr_sealed, + NULL, +}; + +static struct configfs_group_operations cfg_snd_avirt_stream_group_ops = { + .make_item = cfg_snd_avirt_stream_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_avirt_group_type = { + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem cfg_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "snd-avirt", + .ci_type = &cfg_avirt_group_type, + }, + }, +}; + +int __init snd_avirt_configfs_init(struct snd_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) { + D_ERRORK("Cannot register configfs subsys!"); + 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); + D_ERRORK("Cannot register configfs default group 'streams'!"); + goto exit_configfs; + } + + return 0; + +exit_configfs: + configfs_unregister_subsystem(&cfg_subsys); + + return err; +} + +void __exit snd_avirt_configfs_exit(struct snd_avirt_core *core) +{ + configfs_unregister_default_group(core->stream_group); + configfs_unregister_subsystem(&cfg_subsys); +} |