nuttx/audio/audio_comp.c
Gregory Nutt dc8c814ca3 Squashed commit of the following:
Fixed coding standard error in several files.  Use of while( is incorrect; a space is required between while and (.  Also ran tools/nxstyle and fix thoses complaints as well in most files.

    Changes to comply with coding standard.  Mostly focused on files with missing space after keyword in if(, switch(, and for(.  Offending files also got changes to comply with tools nxstyle.  If there were logs of nxstyle complaints, the file also got a taste of tools/indent.sh.  Still need to fix occurrences of while( with missing space.  There are a lot of them.
2019-02-27 08:41:08 -06:00

1003 lines
28 KiB
C

/****************************************************************************
* 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 <xiaoxiang@pinecone.net>
*
* 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 <stdarg.h>
#include <nuttx/audio/audio.h>
#include <nuttx/audio/audio_comp.h>
#include <nuttx/kmalloc.h>
/****************************************************************************
* 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;
}