diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | alsa-pcm.c | 82 | ||||
-rwxr-xr-x | alsa-pcm.h | 28 | ||||
-rw-r--r-- | configfs.c | 229 | ||||
-rw-r--r-- | core.c | 354 | ||||
-rw-r--r-- | core.h | 76 | ||||
-rw-r--r-- | core_internal.h | 50 | ||||
-rw-r--r-- | dummy/dummy.c | 22 | ||||
-rw-r--r-- | utils.h | 50 |
9 files changed, 548 insertions, 344 deletions
@@ -2,6 +2,7 @@ obj-$(CONFIG_AVIRT) += avirt_core.o avirt_core-y := core.o avirt_core-y += alsa-pcm.o +avirt_core-y += configfs.o ifeq ($(CONFIG_AVIRT_BUILDLOCAL),) CCFLAGS_AVIRT := "drivers/staging/" @@ -7,46 +7,30 @@ * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd */ -#include "alsa-pcm.h" -#include "utils.h" - -#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ - do { \ - if (ap->pcm_ops->callback) { \ - return ap->pcm_ops->callback((substream), \ - ##__VA_ARGS__); \ - } \ +#include "core_internal.h" + +#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ + do { \ + if ((ap)->pcm_ops->callback) { \ + return (ap)->pcm_ops->callback((substream), \ + ##__VA_ARGS__); \ + } \ } while (0) /** - * pcm_buff_complete_cb - PCM buffer complete callback + * avirt_pcm_period_elapsed - PCM buffer complete callback * @substreamid: pointer to ALSA PCM substream - * @return 0 on success or error code otherwise * * This should be called from a child Audio Path once it has finished processing * the pcm buffer */ -int pcm_buff_complete_cb(struct snd_pcm_substream *substream) +void avirt_pcm_period_elapsed(struct snd_pcm_substream *substream) { // Notify ALSA middle layer of the elapsed period boundary snd_pcm_period_elapsed(substream); - - return 0; } +EXPORT_SYMBOL_GPL(avirt_pcm_period_elapsed); -static struct avirt_stream_group *avirt_stream_get_group(int direction) -{ - switch (direction) { - case SNDRV_PCM_STREAM_PLAYBACK: - return &coreinfo.playback; - case SNDRV_PCM_STREAM_CAPTURE: - return &coreinfo.capture; - default: - pr_err("[%s] Direction must be SNDRV_PCM_STREAM_XXX!", - __func__); - return NULL; - } -} /******************************************************************************* * ALSA PCM Callbacks @@ -63,13 +47,14 @@ static struct avirt_stream_group *avirt_stream_get_group(int direction) static int pcm_open(struct snd_pcm_substream *substream) { struct avirt_audiopath *audiopath; - struct avirt_stream_group *group; + struct avirt_stream *stream; struct snd_pcm_hardware *hw; unsigned int bytes_per_sample = 0, blocksize = 0, chans = 0; char *uid = "ap_fddsp"; // TD MF: Make this dynamic! audiopath = avirt_audiopath_get(uid); - CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", uid); + CHK_NULL_V(audiopath, -EFAULT, "Cannot find Audio Path uid: '%s'!", + uid); substream->private_data = audiopath; blocksize = audiopath->blocksize; @@ -87,20 +72,12 @@ static int pcm_open(struct snd_pcm_substream *substream) return -EINVAL; } - // Get device group (playback/capture) - group = avirt_stream_get_group(substream->stream); - CHK_NULL(group); - - // Check if substream id is valid - pr_info("%d substream is < %d", substream->pcm->device, group->devices); - if (substream->pcm->device >= group->devices) { - pr_err("%s %d substream id is invalid expecting %d", __func__, - substream->pcm->device, group->devices); - return -1; - } + stream = __avirt_stream_find_by_device(substream->pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + return PTR_ERR(stream); // Setup remaining hw properties - chans = group->streams[substream->pcm->device].channels; + chans = stream->channels; hw->channels_min = chans; hw->channels_max = chans; hw->buffer_bytes_max = @@ -145,18 +122,17 @@ static int pcm_close(struct snd_pcm_substream *substream) static int pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - int channels, err; + int err; size_t bufsz; struct avirt_audiopath *audiopath; - struct avirt_stream_group *group; - - group = avirt_stream_get_group(substream->stream); - CHK_NULL(group); + struct avirt_stream *stream; - channels = group->streams[substream->pcm->device].channels; + stream = __avirt_stream_find_by_device(substream->pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + return PTR_ERR(stream); - if ((params_channels(hw_params) > channels) || - (params_channels(hw_params) < channels)) { + if ((params_channels(hw_params) > stream->channels) + || (params_channels(hw_params) < stream->channels)) { pr_err("Requested number of channels not supported.\n"); return -EINVAL; } @@ -306,11 +282,11 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) { - //struct snd_pcm_runtime *runtime; - //int offset; + // struct snd_pcm_runtime *runtime; + // int offset; - //runtime = substream->runtime; - //offset = frames_to_bytes(runtime, pos); + // runtime = substream->runtime; + // offset = frames_to_bytes(runtime, pos); // Do additional Audio Path 'copy_user' callback DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), diff --git a/alsa-pcm.h b/alsa-pcm.h deleted file mode 100755 index c45e2a7..0000000 --- a/alsa-pcm.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ALSA Virtual Soundcard - * - * alsa-pcm.h - AVIRT ALSA PCM interface - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd - */ - -#ifndef __AVIRT_ALSA_PCM_H__ -#define __AVIRT_ALSA_PCM_H__ - -#include "core.h" - -extern struct avirt_coreinfo coreinfo; -extern struct snd_pcm_ops pcm_ops; - -/** - * pcm_buff_complete_cb - PCM buffer complete callback - * @substream: pointer to ALSA PCM substream - * @return 0 on success or error code otherwise - * - * This should be called from a child Audio Path once it has finished processing - * the PCM buffer - */ -int pcm_buff_complete_cb(struct snd_pcm_substream *substream); - -#endif // __AVIRT_ALSA_PCM_H__ 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); +} @@ -8,14 +8,12 @@ */ #include <linux/module.h> -#include <linux/platform_device.h> #include <linux/string.h> #include <sound/core.h> #include <sound/initval.h> +#include <avirt/core.h> -#include "avirt/core.h" -#include "alsa-pcm.h" -#include "utils.h" +#include "core_internal.h" MODULE_AUTHOR("JOSHANNE <james.oshannessy@fiberdyne.com.au>"); MODULE_AUTHOR("MFARRUGI <mark.farrugia@fiberdyne.com.au>"); @@ -29,162 +27,19 @@ MODULE_LICENSE("GPL v2"); #define D_ERRORK(fmt, args...) DERROR(AP_LOGNAME, fmt, ##args) #define SND_AVIRTUAL_DRIVER "snd_avirt" -#define MAX_PCM_DEVS 8 -#define MAX_AUDIOPATHS 4 -#define DEFAULT_FILE_PERMS 0644 - -/* Number of playback devices to create (max = MAX_PCM_DEVS) */ -static unsigned int playback_num; -/* Number of capture devices to create (max = MAX_PCM_DEVS) */ -static unsigned int capture_num; -/* Names per playback device */ -static char *playback_names[MAX_PCM_DEVS]; -/* Names per capture device */ -static char *capture_names[MAX_PCM_DEVS]; -/* Channels per playback device */ -static unsigned int playback_chans[MAX_PCM_DEVS]; -/* Channels per capture device */ -static unsigned int capture_chans[MAX_PCM_DEVS]; - -module_param(playback_num, int, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(playback_num, - "Number of playback devices to create (max = MAX_PCM_DEVS)"); -module_param(capture_num, int, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(capture_num, - "Number of capture devices to create (max = MAX_PCM_DEVS)"); -module_param_array(playback_names, charp, NULL, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(playback_names, "Names per playback device"); -module_param_array(capture_names, charp, NULL, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(capture_names, "Names per capture device"); -module_param_array(playback_chans, int, NULL, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(playback_chans, "Channels per playback device"); -module_param_array(capture_chans, int, NULL, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(capture_chans, "Channels per capture device"); - -static struct avirt_core { - struct snd_card *card; - struct device *dev; - struct class *avirt_class; - struct platform_device *platform_dev; -} core; + +extern struct snd_pcm_ops pcm_ops; + +static struct avirt_core core = { + .stream_count = 0, +}; struct avirt_coreinfo coreinfo = { - .version = { 0, 0, 1 }, - .pcm_buff_complete = pcm_buff_complete_cb, + .version = {0, 0, 1}, }; static LIST_HEAD(audiopath_list); -/** - * avirt_probe - Register ALSA soundcard - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -static int avirt_probe(struct platform_device *devptr) -{ - static struct snd_device_ops device_ops; - struct snd_pcm **pcm; - int err = 0, i = 0; - - if (playback_num == 0 && capture_num == 0) { - pr_err("playback_num or capture_num must be greater than 0!\n"); - return -EINVAL; - } - - // Create the card instance - CHK_ERR_V(snd_card_new(&devptr->dev, SNDRV_DEFAULT_IDX1, "avirt", - THIS_MODULE, 0, &core.card), - "Failed to create sound card"); - - strcpy(core.card->driver, "avirt-alsa-device"); - strcpy(core.card->shortname, "avirt"); - strcpy(core.card->longname, "A virtual sound card driver for ALSA"); - - // Create new sound device - CHK_ERR_V((snd_device_new(core.card, SNDRV_DEV_LOWLEVEL, &coreinfo, - &device_ops)), - "Failed to create sound device"); - - // TEMP - if (playback_num > 0) { - coreinfo.playback.devices = playback_num; - coreinfo.playback.streams = kzalloc( - sizeof(*coreinfo.playback.streams) * playback_num, - GFP_KERNEL); - for (i = 0; i < playback_num; i++) { - pcm = &coreinfo.playback.streams[i].pcm; - CHK_ERR(snd_pcm_new(core.card, playback_names[i], i, 1, - 0, pcm)); - - /** Register driver callbacks */ - snd_pcm_set_ops(*pcm, SNDRV_PCM_STREAM_PLAYBACK, - &pcm_ops); - - (*pcm)->info_flags = 0; - strcpy((*pcm)->name, playback_names[i]); - coreinfo.playback.streams[i].channels = - playback_chans[i]; - pr_info("snd_pcm_new: name: %s, chans: %d\n", - (*pcm)->name, - coreinfo.playback.streams[i].channels); - } - } - - if (capture_num > 0) { - coreinfo.capture.devices = capture_num; - coreinfo.capture.streams = - kzalloc(sizeof(*coreinfo.capture.streams) * capture_num, - GFP_KERNEL); - for (i = 0; i < capture_num; i++) { - pcm = &coreinfo.capture.streams[i].pcm; - CHK_ERR(snd_pcm_new(core.card, capture_names[i], - i + playback_num, 0, 1, pcm)); - - /** Register driver callbacks */ - snd_pcm_set_ops(*pcm, SNDRV_PCM_STREAM_CAPTURE, - &pcm_ops); - - (*pcm)->info_flags = 0; - strcpy((*pcm)->name, capture_names[i]); - coreinfo.capture.streams[i].channels = capture_chans[i]; - pr_info("snd_pcm_new: name: %s, chans: %d\n", - (*pcm)->name, - coreinfo.capture.streams[i].channels); - } - } - // TEMP - - /** Register with the ALSA framework */ - CHK_ERR_V(snd_card_register(core.card), "Device registration failed"); - - return err; -} - -/** - * avirt_remove - Deregister ALSA soundcard - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -static int avirt_remove(struct platform_device *devptr) -{ - snd_card_free(core.card); - if (coreinfo.playback.streams) - kfree(coreinfo.playback.streams); - if (coreinfo.capture.streams) - kfree(coreinfo.capture.streams); - - return 0; -} - -static struct platform_driver avirt_driver = { - .probe = avirt_probe, - .remove = avirt_remove, - .driver = - { - .name = SND_AVIRTUAL_DRIVER, - }, -}; - struct avirt_audiopath_obj { struct kobject kobj; struct list_head list; @@ -195,7 +50,7 @@ static struct kset *avirt_audiopath_kset; static struct kobject *kobj; #define to_audiopath_obj(d) container_of(d, struct avirt_audiopath_obj, kobj) -#define to_audiopath_attr(a) \ +#define to_audiopath_attr(a) \ container_of(a, struct avirt_audiopath_attribute, attr) /** @@ -350,8 +205,10 @@ static void destroy_avirt_audiopath_obj(struct avirt_audiopath_obj *p) struct avirt_audiopath *avirt_audiopath_get(const char *uid) { struct 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) + { + // pr_info("get_ap %s\n", ap_obj->path->uid); if (!strcmp(ap_obj->path->uid, uid)) return ap_obj->path; } @@ -424,12 +281,121 @@ int avirt_audiopath_deregister(struct avirt_audiopath *audiopath) EXPORT_SYMBOL_GPL(avirt_audiopath_deregister); /** - * avirt_unregister_all - Unregister the platform device driver + * avirt_stream_count - get the stream count for the given direction + * @direction: The direction to get the stream count for + * @return: The stream count */ -static void avirt_unregister_all(void) +int avirt_stream_count(unsigned int direction) { - platform_device_unregister(core.platform_dev); - platform_driver_unregister(&avirt_driver); + struct list_head *entry; + struct config_item *item; + struct avirt_stream *stream; + unsigned int count = 0; + + if (direction > 1) + return -ERANGE; + + list_for_each(entry, &core.stream_group->cg_children) + { + item = container_of(entry, struct config_item, ci_entry); + stream = avirt_stream_from_config_item(item); + if (!stream) + return -EFAULT; + if (stream->direction == direction) + count++; + } + + return count; +} +EXPORT_SYMBOL_GPL(avirt_stream_count); + +/** + * __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 + * SNDRV_PCM_STREAM_CAPTURE) + * @return: The newly created audio stream if successful, or an error pointer + */ +struct avirt_stream *__avirt_stream_create(const char *name, int direction) +{ + struct snd_pcm *pcm; + struct avirt_stream *stream; + int err; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return ERR_PTR(-ENOMEM); + + strcpy(stream->name, name); + strcpy(stream->map, "ap_fddsp"); + stream->channels = 0; + stream->direction = direction; + stream->device = core.stream_count++; + + err = snd_pcm_new(core.card, name, stream->device, !direction, + direction, &pcm); + if (err < 0) + return ERR_PTR(err); + + // TD MF: IMPORTANT: NEED TO TEST >8 PCM DEVICES ON A + // CARD! + /** Register driver callbacks */ + snd_pcm_set_ops(pcm, direction, &pcm_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, name); + + pr_info("%s [ARGS] name: %s device:%d\n", __func__, name, + stream->device); + + // coreinfo.streams[stream_idx] = stream; + + return stream; +} + +int __avirt_card_register(void) +{ + int err = 0; + + struct avirt_audiopath_obj *ap_obj; + list_for_each_entry(ap_obj, &audiopath_list, list) + { + pr_info("Calling configure for AP uid: %s\n", + ap_obj->path->uid); + ap_obj->path->configure(core.stream_group, core.stream_count); + } + + err = snd_card_register(core.card); + if (err < 0) { + pr_err("Sound card registration failed!"); + snd_card_free(core.card); + } + + return err; +} + +struct avirt_stream *__avirt_stream_find_by_device(unsigned int device) +{ + struct avirt_stream *stream; + struct config_item *item; + struct list_head *entry; + + if (device >= core.stream_count) { + pr_err("Stream device number is greater than number streams available\n"); + return ERR_PTR(-EINVAL); + } + + list_for_each(entry, &core.stream_group->cg_children) + { + item = container_of(entry, struct config_item, ci_entry); + stream = avirt_stream_from_config_item(item); + if (!stream) + return ERR_PTR(-EFAULT); + if (stream->device == device) + return stream; + } + + return NULL; } /** @@ -442,23 +408,10 @@ static int __init core_init(void) D_INFOK("Alsa Virtual Sound Driver avirt-%d.%d.%d", coreinfo.version[0], coreinfo.version[1], coreinfo.version[2]); - err = platform_driver_register(&avirt_driver); - if (err < 0) - return err; - - core.platform_dev = platform_device_register_simple(SND_AVIRTUAL_DRIVER, - 0, NULL, 0); - if (IS_ERR(core.platform_dev)) { - err = PTR_ERR(core.platform_dev); - pr_err("platform_dev error [%d]!\n", err); - goto exit_platform_device; - } - core.avirt_class = class_create(THIS_MODULE, SND_AVIRTUAL_DRIVER); if (IS_ERR(core.avirt_class)) { pr_err("No udev support\n"); - err = PTR_ERR(core.avirt_class); - goto exit_bus; + return PTR_ERR(core.avirt_class); } core.dev = device_create(core.avirt_class, NULL, 0, NULL, "avirtcore"); @@ -467,22 +420,38 @@ static int __init core_init(void) goto exit_class; } + // Create the card instance + err = snd_card_new(core.dev, SNDRV_DEFAULT_IDX1, "avirt", THIS_MODULE, + 0, &core.card); + if (err < 0) { + pr_err("Failed to create sound card"); + goto exit_class_container; + } + + strcpy(core.card->driver, "avirt-alsa-device"); + strcpy(core.card->shortname, "avirt"); + strcpy(core.card->longname, "A virtual sound card driver for ALSA"); + avirt_audiopath_kset = kset_create_and_add("audiopaths", NULL, &core.dev->kobj); if (!avirt_audiopath_kset) { err = -ENOMEM; - goto exit_class_container; + goto exit_snd_card; } + err = __avirt_configfs_init(&core); + if (err < 0) + goto exit_snd_card; + return 0; +exit_snd_card: + snd_card_free(core.card); exit_class_container: device_destroy(core.avirt_class, 0); exit_class: class_destroy(core.avirt_class); -exit_bus: -exit_platform_device: - avirt_unregister_all(); + return err; } @@ -491,34 +460,11 @@ exit_platform_device: */ static void __exit core_exit(void) { + __avirt_configfs_exit(&core); + kset_unregister(avirt_audiopath_kset); device_destroy(core.avirt_class, 0); class_destroy(core.avirt_class); - - avirt_unregister_all(); - D_INFOK("Exit begin!"); - pr_info("playback_num: %d, capture_num: %d\n", playback_num, - capture_num); - - pr_info("playback_chans: %d %d %d %d %d %d %d %d\n", playback_chans[0], - playback_chans[1], playback_chans[2], playback_chans[3], - playback_chans[4], playback_chans[5], playback_chans[6], - playback_chans[7]); - - pr_info("capture_chans: %d %d %d %d %d %d %d %d\n", capture_chans[0], - capture_chans[1], capture_chans[2], capture_chans[3], - capture_chans[4], capture_chans[5], capture_chans[6], - capture_chans[7]); - - pr_info("playback_names: %s %s %s %s %s %s %s %s\n", playback_names[0], - playback_names[1], playback_names[2], playback_names[3], - playback_names[4], playback_names[5], playback_names[6], - playback_names[7]); - - pr_info("capture_names: %s %s %s %s %s %s %s %s\n", capture_names[0], - capture_names[1], capture_names[2], capture_names[3], - capture_names[4], capture_names[5], capture_names[6], - capture_names[7]); } module_init(core_init); @@ -13,26 +13,28 @@ #include <sound/pcm.h> #include <linux/configfs.h> -#define MAX_NAME_LEN 32 +#define MAX_STREAMS 16 +#define MAX_NAME_LEN 80 /** - * PCM buffer complete callback - * - * These are called from the audiopath when a PCM buffer has completed, and - * new data can be submitted/retrieved + * AVIRT Audio Path configure function type + * Each Audio Path registers this at avirt_audiopath_register time. + * It is then called by the core once AVIRT has been configured */ -typedef int (*avirt_buff_complete)(struct snd_pcm_substream *substream); +typedef int (*avirt_audiopath_configure)(struct config_group *stream_group, + unsigned int stream_count); /** * AVIRT Audio Path info */ struct avirt_audiopath { - const char *uid; /* Unique identifier */ - const char *name; /* Pretty name */ - unsigned int version[3]; /* Version - Major.Minor.Ext */ - struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ - struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ - unsigned int blocksize; /* Audio frame size accepted */ + const char *uid; /* Unique identifier */ + const char *name; /* Pretty name */ + unsigned int version[3]; /* Version - Major.Minor.Ext */ + struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ + struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ + unsigned int blocksize; /* Audio frame size accepted */ + avirt_audiopath_configure configure; /* Configure callback function */ void *context; }; @@ -41,29 +43,19 @@ struct avirt_audiopath { * Audio stream configuration */ struct avirt_stream { - struct snd_pcm *pcm; /* Stream PCM device */ - unsigned int channels; /* Stream channel count */ + char name[MAX_NAME_LEN]; /* Stream name */ + char map[MAX_NAME_LEN]; /* Stream Audio Path mapping */ + unsigned int channels; /* Stream channel count */ + unsigned int device; /* Stream PCM device no. */ + unsigned int direction; /* Stream direction */ struct config_item item; /* configfs item reference */ }; /** - * Collection of audio streams - */ -struct avirt_stream_group { - struct avirt_stream *streams; /* AVIRT stream array */ - unsigned int devices; /* Number of stream devices */ -}; - -/** * AVIRT core info */ struct avirt_coreinfo { unsigned int version[3]; - - struct avirt_stream_group playback; - struct avirt_stream_group capture; - - avirt_buff_complete pcm_buff_complete; }; /** @@ -90,12 +82,30 @@ int avirt_audiopath_deregister(struct avirt_audiopath *audiopath); struct avirt_audiopath *avirt_audiopath_get(const char *uid); /** - * avirt_subscribe_stream - subscribe the Audio Path to the given streams - * @audiopath: Audio Path to subscribe for - * @streams: The streams to subscribe the Audio Path to - * return: 0 on success or error code otherwise + * avirt_stream_count - get the stream count for the given direction + * @direction: The direction to get the stream count for + * @return: The stream count + */ +int avirt_stream_count(unsigned int direction); + +/** + * avirt_stream_from_config_item - Convert a config_item to an avirt_stream + * @item: The config_item to convert from + * @return: The item's avirt_stream if successful, NULL otherwise + */ +static inline struct avirt_stream * +avirt_stream_from_config_item(struct config_item *item) +{ + return item ? container_of(item, struct avirt_stream, item) : NULL; +} + +/** + * avirt_pcm_period_elapsed - PCM buffer complete callback + * @substream: pointer to ALSA PCM substream + * + * This should be called from a child Audio Path once it has finished processing + * the PCM buffer */ -int avirt_subscribe_stream(struct avirt_audiopath *audiopath, - const char **streams); +void avirt_pcm_period_elapsed(struct snd_pcm_substream *substream); #endif // __AVIRT_CORE_H__ diff --git a/core_internal.h b/core_internal.h new file mode 100644 index 0000000..3639d6e --- /dev/null +++ b/core_internal.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA Virtual Soundcard + * + * core.h - Internal header for virtual ALSA card + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#ifndef __AVIRT_CORE_INTERNAL_H__ +#define __AVIRT_CORE_INTERNAL_H__ + +#include <avirt/core.h> + +#include "utils.h" + +struct avirt_core { + struct snd_card *card; + struct device *dev; + struct class *avirt_class; + struct config_group *stream_group; + unsigned int stream_count; +}; + +int __init __avirt_configfs_init(struct avirt_core *core); +void __exit __avirt_configfs_exit(struct avirt_core *core); + +/** + * __avirt_card_register - Register the sound card to user space + * @return: 0 on success, negative ERRNO on failure + */ +int __avirt_card_register(void); + +/** + * __avirt_stream_find_by_device - Get audio stream from device number + * @device: The PCM device number corresponding to the desired stream + * @return: The audio stream if found, or an error pointer otherwise + */ +struct avirt_stream *__avirt_stream_find_by_device(unsigned int device); + +/** + * __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 + * SNDRV_PCM_STREAM_CAPTURE) + * @return: The newly created audio stream if successful, or an error pointer + */ +struct avirt_stream *__avirt_stream_create(const char *name, int direction); + +#endif // __AVIRT_CORE_INTERNAL_H__ diff --git a/dummy/dummy.c b/dummy/dummy.c index d619786..606b22d 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -130,7 +130,7 @@ static void dummy_systimer_callback(struct timer_list *t) dpcm->elapsed = 0; spin_unlock_irqrestore(&dpcm->lock, flags); if (elapsed) - coreinfo->pcm_buff_complete(dpcm->substream); + avirt_pcm_period_elapsed(dpcm->substream); } static snd_pcm_uframes_t @@ -231,6 +231,25 @@ static struct snd_pcm_ops dummyap_pcm_ops = { /******************************************************************************* * Dummy Audio Path AVIRT registration ******************************************************************************/ +int dummy_configure(struct config_group *avirt_stream_group, + unsigned int stream_count) +{ + // Do something with streams + + struct list_head *entry; + list_for_each(entry, &avirt_stream_group->cg_children) + { + struct config_item *item = + container_of(entry, struct config_item, ci_entry); + struct avirt_stream *stream = + avirt_stream_from_config_item(item); + pr_info("%s: stream name:%s device:%d channels:%d\n", __func__, + stream->name, stream->device, stream->channels); + } + + return 0; +} + static struct snd_pcm_hardware dummyap_hw = { .formats = SNDRV_PCM_FMTBIT_S16_LE, .info = (SNDRV_PCM_INFO_INTERLEAVED // Channel interleaved audio @@ -250,6 +269,7 @@ static struct avirt_audiopath dummyap_module = { .hw = &dummyap_hw, .pcm_ops = &dummyap_pcm_ops, .blocksize = DUMMY_BLOCKSIZE, + .configure = dummy_configure, }; static int __init dummy_init(void) @@ -12,38 +12,38 @@ #include <linux/slab.h> -#define PRINT_ERR(errno, errmsg) \ - pr_err("[%s]:[ERRNO:%d]: %s ", __func__, errno, (errmsg)); +#define PRINT_ERR(errno, errmsg) \ + pr_err("[%s]:[ERRNO:%d]: %s \n", __func__, errno, (errmsg)); -#define CHK_ERR(errno) \ - do { \ - if ((errno) < 0) \ - return (errno); \ +#define CHK_ERR(errno) \ + do { \ + if ((errno) < 0) \ + return (errno); \ } while (0) -#define CHK_ERR_V(errno, errmsg, ...) \ - do { \ - if ((errno) < 0) { \ - PRINT_ERR((errno), (errmsg), ##__VA_ARGS__) \ - return (errno); \ - } \ +#define CHK_ERR_V(errno, errmsg, ...) \ + do { \ + if ((errno) < 0) { \ + PRINT_ERR((errno), (errmsg), ##__VA_ARGS__) \ + return (errno); \ + } \ } while (0) -#define CHK_NULL(x) \ - do { \ - if (!(x)) \ - return -EFAULT; \ +#define CHK_NULL(x, errno) \ + do { \ + if (!(x)) \ + return errno; \ } while (0) -#define CHK_NULL_V(x, errmsg, ...) \ - do { \ - if (!(x)) { \ - char *errmsg_done = \ - kasprintf(GFP_KERNEL, errmsg, ##__VA_ARGS__); \ - PRINT_ERR(EFAULT, errmsg_done); \ - kfree(errmsg_done); \ - return -EFAULT; \ - } \ +#define CHK_NULL_V(x, errno, errmsg, ...) \ + do { \ + if (!(x)) { \ + char *errmsg_done = \ + kasprintf(GFP_KERNEL, errmsg, ##__VA_ARGS__); \ + PRINT_ERR(EFAULT, errmsg_done); \ + kfree(errmsg_done); \ + return errno; \ + } \ } while (0) #endif |