1002 lines
28 KiB
C
1002 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;
|
|
}
|