diff --git a/audio/Kconfig b/audio/Kconfig index 941b9d2f88..251b54c7a7 100644 --- a/audio/Kconfig +++ b/audio/Kconfig @@ -11,6 +11,12 @@ config AUDIO if AUDIO +config AUDIO_COMP + bool "Support audio composition" + default n + ---help--- + Composite several lower level audio devices into big one. + config AUDIO_MULTI_SESSION bool "Support multiple sessions" default n diff --git a/audio/Makefile b/audio/Makefile index 42ee4ea319..fef3f261c8 100644 --- a/audio/Makefile +++ b/audio/Makefile @@ -45,6 +45,10 @@ ASRCS = CSRCS = audio.c VPATH = . +ifeq ($(CONFIG_AUDIO_COMP),y) + CSRCS += audio_comp.c +endif + # Include support for various drivers. Each Make.defs file will add its # files to the source file list, add its DEPPATH info, and will add # the appropriate paths to the VPATH variable diff --git a/audio/audio_comp.c b/audio/audio_comp.c new file mode 100644 index 0000000000..14b77a7370 --- /dev/null +++ b/audio/audio_comp.c @@ -0,0 +1,1001 @@ +/**************************************************************************** + * audio/audio_comp.c + * + * A general audio driver to composite other lower level audio driver. + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * Author: Xiang Xiao + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes the internal state of the audio composite */ + +struct audio_comp_priv_s +{ + /* This is is our appearance to the outside world. This *MUST* be the + * first element of the structure so that we can freely cast between + * types struct audio_lowerhalf and struct audio_comp_dev_s. + */ + + struct audio_lowerhalf_s export; + + /* This is the contained, low-level audio device array and count. */ + + FAR struct audio_lowerhalf_s **lower; + int count; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int audio_comp_getcaps(FAR struct audio_lowerhalf_s *dev, int type, + FAR struct audio_caps_s *caps); +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_configure(FAR struct audio_lowerhalf_s *dev, + FAR void *session, + FAR const struct audio_caps_s *caps); +#else +static int audio_comp_configure(FAR struct audio_lowerhalf_s *dev, + FAR const struct audio_caps_s *caps); +#endif +static int audio_comp_shutdown(FAR struct audio_lowerhalf_s *dev); +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_start(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#else +static int audio_comp_start(FAR struct audio_lowerhalf_s *dev); +#endif +#ifndef CONFIG_AUDIO_EXCLUDE_STOP +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_stop(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#else +static int audio_comp_stop(FAR struct audio_lowerhalf_s *dev); +#endif +#endif +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_pause(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +static int audio_comp_resume(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#else +static int audio_comp_pause(FAR struct audio_lowerhalf_s *dev); +static int audio_comp_resume(FAR struct audio_lowerhalf_s *dev); +#endif +#endif +static int audio_comp_allocbuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct audio_buf_desc_s *bufdesc); +static int audio_comp_freebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct audio_buf_desc_s *bufdesc); +static int audio_comp_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb); +static int audio_comp_cancelbuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb); +static int audio_comp_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, + unsigned long arg); +static int audio_comp_read(FAR struct audio_lowerhalf_s *dev, + FAR char *buffer, size_t buflen); +static int audio_comp_write(FAR struct audio_lowerhalf_s *dev, + FAR const char *buffer, size_t buflen); +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_reserve(FAR struct audio_lowerhalf_s *dev, + FAR void **session); +#else +static int audio_comp_reserve(FAR struct audio_lowerhalf_s *dev); +#endif +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_release(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#else +static int audio_comp_release(FAR struct audio_lowerhalf_s *dev); +#endif + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static void audio_comp_callback(FAR void *arg, uint16_t reason, + FAR struct ap_buffer_s *apb, uint16_t status, + FAR void *session); +#else +static void audio_comp_callback(FAR void *arg, uint16_t reason, + FAR struct ap_buffer_s *apb, uint16_t status); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct audio_ops_s g_audio_comp_ops = +{ + audio_comp_getcaps, /* getcaps */ + audio_comp_configure, /* configure */ + audio_comp_shutdown, /* shutdown */ + audio_comp_start, /* start */ +#ifndef CONFIG_AUDIO_EXCLUDE_STOP + audio_comp_stop, /* stop */ +#endif +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME + audio_comp_pause, /* pause */ + audio_comp_resume, /* resume */ +#endif + audio_comp_allocbuffer, /* allocbuffer */ + audio_comp_freebuffer, /* freebuffer */ + audio_comp_enqueuebuffer, /* enqueue_buffer */ + audio_comp_cancelbuffer, /* cancel_buffer */ + audio_comp_ioctl, /* ioctl */ + audio_comp_read, /* read */ + audio_comp_write, /* write */ + audio_comp_reserve, /* reserve */ + audio_comp_release /* release */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: audio_comp_getcaps + * + * Description: Get the audio device capabilities + * + ****************************************************************************/ + +static int audio_comp_getcaps(FAR struct audio_lowerhalf_s *dev, int type, + FAR struct audio_caps_s *caps) +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; + int ret = -ENOTTY; + int i; + + caps->ac_channels = 0; + caps->ac_format.hw = 0; + caps->ac_controls.w = 0; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->getcaps) + { + FAR struct audio_caps_s dup = *caps; + + int tmp = lower[i]->ops->getcaps(lower[i], type, &dup); + if (tmp == -ENOTTY) + { + continue; + } + + ret = tmp; + if (ret < 0) + { + break; + } + + if (caps->ac_channels < dup.ac_channels) + { + caps->ac_channels = dup.ac_channels; + } + + caps->ac_format.hw |= dup.ac_format.hw; + caps->ac_controls.w |= dup.ac_controls.w; + } + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_configure + * + * Description: + * Configure the audio device for the specified mode of operation. + * + ****************************************************************************/ + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_configure(FAR struct audio_lowerhalf_s *dev, + FAR void *session, + FAR const struct audio_caps_s *caps) +#else +static int audio_comp_configure(FAR struct audio_lowerhalf_s *dev, + FAR const struct audio_caps_s *caps) +#endif +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; +#ifdef CONFIG_AUDIO_MULTI_SESSION + FAR void **sess = session; +#endif + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->configure) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + int tmp = lower[i]->ops->configure(lower[i], sess[i], caps); +#else + int tmp = lower[i]->ops->configure(lower[i], caps); +#endif + if (tmp == -ENOTTY) + { + continue; + } + + ret = tmp; + if (ret < 0) + { + break; + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_shutdown + * + * Description: + * Shutdown the driver and put it in the lowest power state possible. + * + ****************************************************************************/ + +static int audio_comp_shutdown(FAR struct audio_lowerhalf_s *dev) +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; + int ret = -ENOTTY; + int i; + + for (i = priv->count - 1; i >= 0; i--) + { + if (lower[i]->ops->shutdown) + { + int tmp = lower[i]->ops->shutdown(lower[i]); + if (tmp == -ENOTTY) + { + continue; + } + + if (tmp < 0 || ret == -ENOTTY || ret >= 0) + { + ret = tmp; + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_start + * + * Description: + * Start the configured operation (audio streaming, volume enabled, etc.). + * + ****************************************************************************/ + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_start(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_comp_start(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; +#ifdef CONFIG_AUDIO_MULTI_SESSION + FAR void **sess = session; +#endif + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->start) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + int tmp = lower[i]->ops->start(lower[i], sess[i]); +#else + int tmp = lower[i]->ops->start(lower[i]); +#endif + if (tmp == -ENOTTY) + { + continue; + } + + ret = tmp; + if (ret >= 0) + { + continue; + } + + while (--i >= 0) + { + if (lower[i]->ops->stop) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + lower[i]->ops->stop(lower[i], sess[i]); +#else + lower[i]->ops->stop(lower[i]); +#endif + } + } + break; + } + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_stop + * + * Description: Stop the configured operation (audio streaming, volume + * disabled, etc.). + * + ****************************************************************************/ + +#ifndef CONFIG_AUDIO_EXCLUDE_STOP +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_stop(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_comp_stop(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; +#ifdef CONFIG_AUDIO_MULTI_SESSION + FAR void **sess = session; +#endif + int ret = -ENOTTY; + int i; + + for (i = priv->count - 1; i >= 0; i--) + { + if (lower[i]->ops->stop) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + int tmp = lower[i]->ops->stop(lower[i], sess[i]); +#else + int tmp = lower[i]->ops->stop(lower[i]); +#endif + if (tmp == -ENOTTY) + { + continue; + } + + if (tmp < 0 || ret == -ENOTTY || ret >= 0) + { + ret = tmp; + } + } + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: audio_comp_pause + * + * Description: Pauses the playback. + * + ****************************************************************************/ + +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_pause(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_comp_pause(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; +#ifdef CONFIG_AUDIO_MULTI_SESSION + FAR void **sess = session; +#endif + int ret = -ENOTTY; + int i; + + for (i = priv->count - 1; i >= 0; i--) + { + if (lower[i]->ops->pause) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + int tmp = lower[i]->ops->pause(lower[i], sess[i]); +#else + int tmp = lower[i]->ops->pause(lower[i]); +#endif + if (tmp == -ENOTTY) + { + continue; + } + + if (tmp < 0 || ret == -ENOTTY || ret >= 0) + { + ret = tmp; + } + } + } + + return ret; +} +#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */ + +/**************************************************************************** + * Name: audio_comp_resume + * + * Description: Resumes the playback. + * + ****************************************************************************/ + +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_resume(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_comp_resume(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; +#ifdef CONFIG_AUDIO_MULTI_SESSION + FAR void **sess = session; +#endif + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->resume) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + int tmp = lower[i]->ops->resume(lower[i], sess[i]); +#else + int tmp = lower[i]->ops->resume(lower[i]); +#endif + if (tmp == -ENOTTY) + { + continue; + } + + ret = tmp; + if (ret >= 0) + { + continue; + } + + while (--i >= 0) + { + if (lower[i]->ops->pause) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + lower[i]->ops->pause(lower[i], sess[i]); +#else + lower[i]->ops->pause(lower[i]); +#endif + } + } + break; + } + } + + return ret; +} +#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */ + +/**************************************************************************** + * Name: audio_comp_allocbuffer + * + * Description: Allocate an audio pipeline buffer. + * + ****************************************************************************/ + +static int audio_comp_allocbuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct audio_buf_desc_s *bufdesc) +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->allocbuffer) + { + ret = lower[i]->ops->allocbuffer(lower[i], bufdesc); + if (ret != -ENOTTY) + { + break; + } + } + } + + if (ret == -ENOTTY) + { + ret = apb_alloc(bufdesc); + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_freebuffer + * + * Description: Free an audio pipeline buffer. + * + ****************************************************************************/ + +static int audio_comp_freebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct audio_buf_desc_s *bufdesc) +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->freebuffer) + { + ret = lower[i]->ops->freebuffer(lower[i], bufdesc); + if (ret != -ENOTTY) + { + break; + } + } + } + + if (ret == -ENOTTY) + { + apb_free(bufdesc->u.pBuffer); + ret = sizeof(*bufdesc); + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_enqueuebuffer + * + * Description: Enqueue an Audio Pipeline Buffer for playback/ processing. + * + ****************************************************************************/ + +static int audio_comp_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb) +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->enqueuebuffer) + { + ret = lower[i]->ops->enqueuebuffer(lower[i], apb); + if (ret != -ENOTTY) + { + break; + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_cancelbuffer + * + * Description: Called when an enqueued buffer is being cancelled. + * + ****************************************************************************/ + +static int audio_comp_cancelbuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb) +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->cancelbuffer) + { + ret = lower[i]->ops->cancelbuffer(lower[i], apb); + if (ret != -ENOTTY) + { + break; + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_ioctl + * + * Description: Perform a device ioctl + * + ****************************************************************************/ + +static int audio_comp_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->ioctl) + { + int tmp = lower[i]->ops->ioctl(lower[i], cmd, arg); + if (tmp == -ENOTTY) + { + continue; + } + + ret = tmp; + if (ret < 0) + { + break; + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_read + * + * Description: Lower-half logic for read commands. + * + ****************************************************************************/ + +static int audio_comp_read(FAR struct audio_lowerhalf_s *dev, + FAR char *buffer, size_t buflen) +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->read) + { + ret = lower[i]->ops->read(lower[i], buffer, buflen); + if (ret != -ENOTTY) + { + break; + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_write + * + * Description: Lower-half logic for write commands. + * + ****************************************************************************/ + +static int audio_comp_write(FAR struct audio_lowerhalf_s *dev, + FAR const char *buffer, size_t buflen) +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; + int ret = -ENOTTY; + int i; + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->write) + { + ret = lower[i]->ops->write(lower[i], buffer, buflen); + if (ret != -ENOTTY) + { + break; + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_reserve + * + * Description: Reserves a session. + * + ****************************************************************************/ + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_reserve(FAR struct audio_lowerhalf_s *dev, + FAR void **session) +#else +static int audio_comp_reserve(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; +#ifdef CONFIG_AUDIO_MULTI_SESSION + FAR void **sess; +#endif + int ret = OK; + int i; + +#ifdef CONFIG_AUDIO_MULTI_SESSION + sess = kmm_calloc(priv->count, sizeof(*sess)); + if (sess == NULL) + { + return -ENOMEM; + } +#endif + + for (i = 0; i < priv->count; i++) + { + if (lower[i]->ops->reserve) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + int tmp = lower[i]->ops->reserve(lower[i], &sess[i]); +#else + int tmp = lower[i]->ops->reserve(lower[i]); +#endif + if (tmp == -ENOTTY) + { + continue; + } + + ret = tmp; + if (ret >= 0) + { + continue; + } + + while (--i >= 0) + { + if (lower[i]->ops->release) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + lower[i]->ops->release(lower[i], sess[i]); +#else + lower[i]->ops->release(lower[i]); +#endif + } + } + +#ifdef CONFIG_AUDIO_MULTI_SESSION + kmm_free(sess); + sess = NULL; +#endif + break; + } + } + +#ifdef CONFIG_AUDIO_MULTI_SESSION + *session = sess; +#endif + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_release + * + * Description: Releases the session. + * + ****************************************************************************/ + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int audio_comp_release(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int audio_comp_release(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct audio_comp_priv_s *priv = (FAR struct audio_comp_priv_s *)dev; + FAR struct audio_lowerhalf_s **lower = priv->lower; +#ifdef CONFIG_AUDIO_MULTI_SESSION + FAR void **sess = session; +#endif + int ret = OK; + int i; + + for (i = priv->count - 1; i >= 0; i--) + { + if (lower[i]->ops->release) + { +#ifdef CONFIG_AUDIO_MULTI_SESSION + int tmp = lower[i]->ops->release(lower[i], sess[i]); +#else + int tmp = lower[i]->ops->release(lower[i]); +#endif + if (tmp == -ENOTTY) + { + continue; + } + + if (tmp < 0 || ret >= 0) + { + ret = tmp; + } + } + } + +#ifdef CONFIG_AUDIO_MULTI_SESSION + kmm_free(sess); +#endif + + return ret; +} + +/**************************************************************************** + * Name: audio_comp_callback + * + * Description: + * Lower-to-upper level callback for buffer dequeueing. + * + * Input Parameters: + * arg - The value of the 'priv' field from audio_lowerhalf_s. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static void audio_comp_callback(FAR void *arg, uint16_t reason, + FAR struct ap_buffer_s *apb, uint16_t status, + FAR void *session) +#else +static void audio_comp_callback(FAR void *arg, uint16_t reason, + FAR struct ap_buffer_s *apb, uint16_t status) +#endif +{ + FAR struct audio_comp_priv_s *priv = arg; + +#ifdef CONFIG_AUDIO_MULTI_SESSION + priv->export.upper(priv->export.priv, reason, apb, status, session); +#else + priv->export.upper(priv->export.priv, reason, apb, status); +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: audio_comp_initialize + * + * Description: + * Initialize the composite audio device. + * + * Input Parameters: + * name - The name of the audio device. + * ... - The list of the lower half audio driver. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + * Note + * The variable argument list must be NULL terminated. + * + ****************************************************************************/ + +int audio_comp_initialize(FAR const char *name, ...) +{ + FAR struct audio_comp_priv_s *priv; + va_list ap; + va_list cp; + int ret = -ENOMEM; + int i; + + va_start(ap, name); + va_copy(cp, ap); + + priv = kmm_zalloc(sizeof(struct audio_comp_priv_s)); + if (priv == NULL) + { + goto end_va; + } + + priv->export.ops = &g_audio_comp_ops; + + while(va_arg(ap, FAR struct audio_lowerhalf_s *)) + { + priv->count++; + } + + priv->lower = kmm_calloc(priv->count, sizeof(FAR struct audio_lowerhalf_s *)); + if (priv->lower == NULL) + { + goto free_priv; + } + + for (i = 0; i < priv->count; i++) + { + FAR struct audio_lowerhalf_s *tmp; + + tmp = va_arg(cp, FAR struct audio_lowerhalf_s *); + tmp->upper = audio_comp_callback; + tmp->priv = priv; + + priv->lower[i] = tmp; + } + + ret = audio_register(name, &priv->export); + if (ret < 0) + { + goto free_lower; + } + + va_end(ap); + return OK; + +free_lower: + kmm_free(priv->lower); +free_priv: + kmm_free(priv); +end_va: + va_end(ap); + return ret; +} diff --git a/include/nuttx/audio/audio_comp.h b/include/nuttx/audio/audio_comp.h new file mode 100644 index 0000000000..b9b57c62fb --- /dev/null +++ b/include/nuttx/audio/audio_comp.h @@ -0,0 +1,99 @@ +/**************************************************************************** + * include/nuttx/audio/audio_comp.h + * A general audio driver to composite other lower level audio driver. + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * Author: Xiang Xiao + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_AUDIO_AUDIO_COMP_H +#define __INCLUDE_NUTTX_AUDIO_AUDIO_COMP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_AUDIO_COMP +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: audio_comp_initialize + * + * Description: + * Initialize the composite audio device. + * + * Input Parameters: + * name - The name of the audio device. + * ... - The list of the lower half audio driver. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + * Note + * The variable argument list must be NULL terminated. + * + ****************************************************************************/ + +int audio_comp_initialize(FAR const char *name, ...); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_AUDIO_COMP */ +#endif /* __INCLUDE_NUTTX_AUDIO_AUDIO_COMP_H */