aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Farrugia <mark.farrugia@fiberdyne.com.au>2018-10-01 17:43:39 +1000
committerMark Farrugia <mark.farrugia@fiberdyne.com.au>2018-10-26 17:27:35 +1100
commit3247d61d378afd8fc76f1e9182e5691bd538ab3f (patch)
tree02485f2a47d8df1c5dbaa11007d6439bac0951e0
parent6c5c0d66a792ecbbf92538a7822b62a36710a341 (diff)
Add configfs interface, revamp stream grouping
Add additional callbacks for audio path configuring, after card has been sealed. Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
-rw-r--r--Makefile1
-rw-r--r--alsa-pcm.c82
-rwxr-xr-xalsa-pcm.h28
-rw-r--r--configfs.c229
-rw-r--r--core.c354
-rw-r--r--core.h76
-rw-r--r--core_internal.h50
-rw-r--r--dummy/dummy.c22
-rw-r--r--utils.h50
9 files changed, 548 insertions, 344 deletions
diff --git a/Makefile b/Makefile
index af92bc5..44874ca 100644
--- a/Makefile
+++ b/Makefile
@@ -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/"
diff --git a/alsa-pcm.c b/alsa-pcm.c
index 21287ee..5a30aff 100644
--- a/alsa-pcm.c
+++ b/alsa-pcm.c
@@ -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);
+}
diff --git a/core.c b/core.c
index c9a9aa3..bf122ed 100644
--- a/core.c
+++ b/core.c
@@ -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);
diff --git a/core.h b/core.h
index c11ee1d..493fc2b 100644
--- a/core.h
+++ b/core.h
@@ -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)
diff --git a/utils.h b/utils.h
index b449938..34e273f 100644
--- a/utils.h
+++ b/utils.h
@@ -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