diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | alsa-pcm.c | 69 | ||||
-rwxr-xr-x | alsa-pcm.h | 28 | ||||
-rwxr-xr-x | alsa.c | 175 | ||||
-rwxr-xr-x | alsa.h | 98 | ||||
-rw-r--r-- | core.c | 118 | ||||
-rw-r--r-- | core.h | 22 | ||||
-rw-r--r-- | utils.h | 49 |
8 files changed, 210 insertions, 350 deletions
@@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_AVIRT) += avirt_core.o avirt_core-y := core.o -avirt_core-y += alsa.o avirt_core-y += alsa-pcm.o ifeq ($(CONFIG_AVIRT_BUILDLOCAL),) @@ -2,12 +2,13 @@ /* * ALSA Virtual Soundcard * - * alsa-pcm.c - ALSA PCM implementation + * alsa-pcm.c - AVIRT ALSA PCM interface * * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd */ -#include "alsa.h" +#include "alsa-pcm.h" +#include "utils.h" #define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ do { \ @@ -17,6 +18,36 @@ } \ } while (0) +/** + * pcm_buff_complete_cb - 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) +{ + // Notify ALSA middle layer of the elapsed period boundary + snd_pcm_period_elapsed(substream); + + return 0; +} + +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 ******************************************************************************/ @@ -31,13 +62,12 @@ */ static int pcm_open(struct snd_pcm_substream *substream) { - struct avirt_alsa_devconfig *config; struct avirt_audiopath *audiopath; - struct avirt_alsa_group *group; + struct avirt_stream_group *group; struct snd_pcm_hardware *hw; - unsigned int bytes_per_sample = 0, blocksize = 0; + unsigned int bytes_per_sample = 0, blocksize = 0, chans = 0; - char *uid = "ap_dummy"; // TD MF: Make this dynamic! + char *uid = "ap_fddsp"; // TD MF: Make this dynamic! audiopath = avirt_get_audiopath(uid); CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", uid); substream->private_data = audiopath; @@ -58,7 +88,7 @@ static int pcm_open(struct snd_pcm_substream *substream) } // Get device group (playback/capture) - group = avirt_alsa_get_group(substream->stream); + group = avirt_stream_get_group(substream->stream); CHK_NULL(group); // Check if substream id is valid @@ -70,13 +100,13 @@ static int pcm_open(struct snd_pcm_substream *substream) } // Setup remaining hw properties - config = &group->config[substream->pcm->device]; - hw->channels_min = config->channels; - hw->channels_max = config->channels; - hw->buffer_bytes_max = blocksize * hw->periods_max * bytes_per_sample * - config->channels; - hw->period_bytes_min = blocksize * bytes_per_sample * config->channels; - hw->period_bytes_max = blocksize * bytes_per_sample * config->channels; + chans = group->streams[substream->pcm->device].channels; + hw->channels_min = chans; + hw->channels_max = chans; + hw->buffer_bytes_max = + blocksize * hw->periods_max * bytes_per_sample * chans; + hw->period_bytes_min = blocksize * bytes_per_sample * chans; + hw->period_bytes_max = blocksize * bytes_per_sample * chans; // Do additional Audio Path 'open' callback DO_AUDIOPATH_CB(audiopath, open, substream); @@ -118,12 +148,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, int channels, err; size_t bufsz; struct avirt_audiopath *audiopath; - struct avirt_alsa_group *group; + struct avirt_stream_group *group; - group = avirt_alsa_get_group(substream->stream); + group = avirt_stream_get_group(substream->stream); CHK_NULL(group); - channels = group->config[substream->pcm->device].channels; + channels = group->streams[substream->pcm->device].channels; if ((params_channels(hw_params) > channels) || (params_channels(hw_params) < channels)) { @@ -252,11 +282,6 @@ static int pcm_get_time_info( struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { - struct avirt_alsa_group *group; - - group = avirt_alsa_get_group(substream->stream); - CHK_NULL(group); - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), get_time_info, substream, system_ts, audio_ts, audio_tstamp_config, audio_tstamp_report); diff --git a/alsa-pcm.h b/alsa-pcm.h new file mode 100755 index 0000000..c45e2a7 --- /dev/null +++ b/alsa-pcm.h @@ -0,0 +1,28 @@ +// 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__ @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ALSA Virtual Soundcard - * - * alsa.c - ALSA PCM driver for virtual ALSA card - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd - */ - -#include <sound/core.h> -#include <sound/initval.h> - -#include "alsa.h" - -static struct snd_card *card; - -/** - * pcm_constructor - Constructs the ALSA PCM middle devices for this driver - * @card: The snd_card struct to construct the devices for - * @return 0 on success or error code otherwise - */ -static int pcm_constructor(struct snd_card *card) -{ - struct snd_pcm *pcm; - int i; - - // Allocate Playback PCM instances - for (i = 0; i < coreinfo.playback.devices; i++) { - CHK_ERR(snd_pcm_new(card, - coreinfo.playback.config[i].devicename, i, - 1, 0, &pcm)); - - /** Register driver callbacks */ - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); - - pcm->info_flags = 0; - strcpy(pcm->name, coreinfo.playback.config[i].devicename); - } - - // Allocate Capture PCM instances - for (i = 0; i < coreinfo.capture.devices; i++) { - CHK_ERR(snd_pcm_new(card, coreinfo.capture.config[i].devicename, - i, 0, 1, &pcm)); - - /** Register driver callbacks */ - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); - - pcm->info_flags = 0; - strcpy(pcm->name, coreinfo.capture.config[i].devicename); - } - - return 0; -} - -/** - * alloc_dev_config - Allocates memory for ALSA device configuration - * @return: 0 on success or error code otherwise - */ -static int alloc_dev_config(struct avirt_alsa_devconfig **devconfig, - struct avirt_alsa_devconfig *userconfig, - unsigned int numdevices) -{ - if (numdevices == 0) - return 0; - - *devconfig = kzalloc(sizeof(**devconfig) * numdevices, GFP_KERNEL); - if (!(*devconfig)) - return -ENOMEM; - - memcpy(*devconfig, userconfig, - sizeof(struct avirt_alsa_devconfig) * numdevices); - - return 0; -} - -struct avirt_alsa_group *avirt_alsa_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; - } -} - -/** - * avirt_alsa_configure_pcm- Configure the PCM device - * @config: Device configuration structure array - * @direction: Direction of PCM (SNDRV_PCM_STREAM_XXX) - * @numdevices: Number of devices (array length) - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_configure_pcm(struct avirt_alsa_devconfig *config, int direction, - unsigned int numdevices) -{ - struct avirt_alsa_group *group; - - group = avirt_alsa_get_group(direction); - CHK_NULL(group); - - CHK_ERR(alloc_dev_config(&group->config, config, numdevices)); - - group->devices = numdevices; - - return 0; -} - -/** - * avirt_alsa_register - Registers the ALSA driver - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_register(struct platform_device *devptr) -{ - static struct snd_device_ops device_ops; - - // Create the card instance - CHK_ERR_V(snd_card_new(&devptr->dev, SNDRV_DEFAULT_IDX1, "avirt", - THIS_MODULE, 0, &card), - "Failed to create sound card"); - - strcpy(card->driver, "avirt-alsa-device"); - strcpy(card->shortname, "avirt"); - strcpy(card->longname, "A virtual sound card driver for ALSA"); - - // Create new sound device - CHK_ERR_V((snd_device_new(card, SNDRV_DEV_LOWLEVEL, &coreinfo, - &device_ops)), - "Failed to create sound device"); - - CHK_ERR((pcm_constructor(card))); - - /** Register with the ALSA framework */ - CHK_ERR_V(snd_card_register(card), "Device registration failed"); - - return 0; -} - -/** - * avirt_alsa_deregister - Deregisters the ALSA driver - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_deregister(void) -{ - CHK_NULL(card); - snd_card_free(card); - CHK_NULL(coreinfo.playback.config); - kfree(coreinfo.playback.config); - CHK_NULL(coreinfo.capture.config); - kfree(coreinfo.capture.config); - - return 0; -} - -/** - * pcm_buff_complete_cb - 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) -{ - // Notify ALSA middle layer of the elapsed period boundary - snd_pcm_period_elapsed(substream); - - return 0; -} @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ALSA Virtual Soundcard - * - * alsa.h - Top level ALSA interface - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd - */ - -#ifndef __AVIRT_ALSA_H__ -#define __AVIRT_ALSA_H__ - -#include "core.h" - -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <sound/pcm.h> - -#define PRINT_ERR(errno, errmsg) \ - pr_err("[%s]:[ERRNO:%d]: %s ", __func__, errno, (errmsg)); - -#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); \ - } \ - } while (0) - -#define CHK_NULL(x) \ - do { \ - if (!(x)) \ - return -EFAULT; \ - } 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; \ - } \ - } while (0) - -extern struct avirt_coreinfo coreinfo; -extern struct snd_pcm_ops pcm_ops; - -/** - * avirt_alsa_configure_pcm- Configure the PCM device - * @config: Device configuration structure array - * @direction: Direction of PCM (SNDRV_PCM_STREAM_XXX) - * @numdevices: Number of devices (array length) - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_configure_pcm(struct avirt_alsa_devconfig *config, int direction, - unsigned int numdevices); - -/** - * avirt_alsa_register - Registers the ALSA driver - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_register(struct platform_device *devptr); - -/** - * avirt_alsa_deregister - Deregisters the ALSA driver - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_deregister(void); - -/** - * avirt_alsa_get_group - Gets the device group for the specified direction - * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE - * @return: Either the playback or capture device group on success, - * or NULL otherwise - */ -struct avirt_alsa_group *avirt_alsa_get_group(int direction); - -/** - * 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_H__ @@ -9,9 +9,13 @@ #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 "alsa.h" +#include "alsa-pcm.h" +#include "utils.h" MODULE_AUTHOR("JOSHANNE <james.oshannessy@fiberdyne.com.au>"); MODULE_AUTHOR("MFARRUGI <mark.farrugia@fiberdyne.com.au>"); @@ -58,8 +62,8 @@ 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; @@ -78,8 +82,8 @@ static LIST_HEAD(audiopath_list); */ static int avirt_probe(struct platform_device *devptr) { - // struct avirt_alsa_devconfig capture_config[MAX_PCM_DEVS]; - struct avirt_alsa_devconfig playback_config[MAX_PCM_DEVS]; + static struct snd_device_ops device_ops; + struct snd_pcm **pcm; int err = 0, i = 0; if (playback_num == 0 && capture_num == 0) { @@ -87,51 +91,71 @@ static int avirt_probe(struct platform_device *devptr) return -EINVAL; } - // Set up playback - for (i = 0; i < playback_num; i++) { - if (!playback_names[i]) { - pr_err("Playback config dev name is NULL for idx=%d\n", - i); - return -EINVAL; - } - memcpy((char *)playback_config[i].devicename, playback_names[i], - MAX_NAME_LEN); - if (playback_chans[i] == 0) { - pr_err("Playback config channels is 0 for idx=%d\n", i); - 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); } - playback_config[i].channels = playback_chans[i]; } - err = avirt_alsa_configure_pcm(playback_config, - SNDRV_PCM_STREAM_PLAYBACK, playback_num); - if (err < 0) - return err; - -// Does not work yet! -#if 0 - // Set up capture - for (i = 0; i < capture_num; i++) { - if (!capture_names[i]) { - pr_err("Capture config devicename is NULL for idx=%d", - i); - return -EINVAL; - } - memcpy((char *)capture_config[i].devicename, capture_names[i], - 255); - if (capture_chans[i] == 0) { - pr_err("Capture config channels is 0 for idx=%d"); - return -EINVAL; + 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); } - capture_config[i].channels = capture_chans[i]; } - err = avirt_alsa_configure_pcm(capture_config, SNDRV_PCM_STREAM_CAPTURE, capture_num); - if (err < 0) - return err; -#endif + // TEMP - // Register for ALSA - CHK_ERR(avirt_alsa_register(devptr)); + /** Register with the ALSA framework */ + CHK_ERR_V(snd_card_register(core.card), "Device registration failed"); return err; } @@ -143,7 +167,13 @@ static int avirt_probe(struct platform_device *devptr) */ static int avirt_remove(struct platform_device *devptr) { - return avirt_alsa_deregister(); + snd_card_free(core.card); + CHK_NULL(coreinfo.playback.streams); + kfree(coreinfo.playback.streams); + CHK_NULL(coreinfo.capture.streams); + kfree(coreinfo.capture.streams); + + return 0; } static struct platform_driver avirt_driver = { @@ -11,6 +11,7 @@ #define __AVIRT_CORE_H__ #include <sound/pcm.h> +#include <linux/configfs.h> #define MAX_NAME_LEN 32 @@ -37,19 +38,20 @@ struct avirt_audiopath { }; /* - * ALSA Substream device configuration + * Audio stream configuration */ -struct avirt_alsa_devconfig { - const char devicename[MAX_NAME_LEN]; - int channels; +struct avirt_stream { + struct snd_pcm *pcm; /* Stream PCM device */ + unsigned int channels; /* Stream channel count */ + struct config_item item; /* configfs item reference */ }; /** - * Collection of ALSA devices + * Collection of audio streams */ -struct avirt_alsa_group { - struct avirt_alsa_devconfig *config; - int devices; +struct avirt_stream_group { + struct avirt_stream *streams; /* AVIRT stream array */ + unsigned int devices; /* Number of stream devices */ }; /** @@ -58,8 +60,8 @@ struct avirt_alsa_group { struct avirt_coreinfo { unsigned int version[3]; - struct avirt_alsa_group playback; - struct avirt_alsa_group capture; + struct avirt_stream_group playback; + struct avirt_stream_group capture; avirt_buff_complete pcm_buff_complete; }; @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA Virtual Soundcard + * + * utils.h - Some useful utilities for AVIRT + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#ifndef __AVIRT_UTILS_H__ +#define __AVIRT_UTILS_H__ + +#include <linux/slab.h> + +#define PRINT_ERR(errno, errmsg) \ + pr_err("[%s]:[ERRNO:%d]: %s ", __func__, errno, (errmsg)); + +#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); \ + } \ + } while (0) + +#define CHK_NULL(x) \ + do { \ + if (!(x)) \ + return -EFAULT; \ + } 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; \ + } \ + } while (0) + +#endif |