include misc/Make.defs
include netdb/Make.defs
include ttrace/Make.defs
+include audio/Make.defs
# REVISIT: Backslash causes problems in $(COBJS) target
DELIM := $(strip /)
--- /dev/null
+###########################################################################
+#
+# Copyright 2017 Samsung Electronics All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+# either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+#
+###########################################################################
+############################################################################
+# libc/audio/Make.defs
+#
+# Copyright (C) 2013 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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.
+#
+############################################################################
+
+ifeq ($(CONFIG_AUDIO),y)
+CSRCS += lib_buffer.c
+
+# Add the audio/ directory to the build
+
+DEPPATH += --dep-path audio
+VPATH += :audio
+endif
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * libc/audio/lib_buffer.c
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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 <tinyara/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <tinyara/audio/audio.h>
+
+#include "lib_internal.h"
+
+#if defined(CONFIG_AUDIO)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: apb_semtake
+ *
+ * Take an Audio Pipeline Buffer.
+ *
+ ****************************************************************************/
+
+static void apb_semtake(FAR struct ap_buffer_s *apb)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&apb->sem) != 0) {
+ /* The only case that an error should occr here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: apb_semgive
+ ****************************************************************************/
+
+#define apb_semgive(b) sem_post(&b->sem)
+
+/****************************************************************************
+ * Name: apb_alloc
+ *
+ * Allocate an Audio Pipeline Buffer for use in the Audio sub-system. This
+ * will perform the actual allocate based on buffer data format, number of
+ * channels, etc. and prepare the buffer for consumption.
+ *
+ ****************************************************************************/
+
+int apb_alloc(FAR struct audio_buf_desc_s *bufdesc)
+{
+ uint32_t bufsize;
+ int ret;
+ struct ap_buffer_s *apb;
+
+ DEBUGASSERT(bufdesc->u.ppBuffer != NULL);
+
+ /* Perform a user mode allocation */
+
+ bufsize = sizeof(struct ap_buffer_s) + bufdesc->numbytes;
+ apb = lib_umalloc(bufsize);
+ *bufdesc->u.ppBuffer = apb;
+
+ /* Test if the allocation was successful or not */
+
+ if (*bufdesc->u.ppBuffer == NULL) {
+ ret = -ENOMEM;
+ } else {
+ /* Populate the buffer contents */
+
+ memset(apb, 0, bufsize);
+ apb->i.channels = 1;
+ apb->crefs = 1;
+ apb->nmaxbytes = bufdesc->numbytes;
+ apb->nbytes = 0;
+ apb->flags = 0;
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ apb->session = bufdesc->session;
+#endif
+
+ sem_init(&apb->sem, 0, 1);
+ ret = sizeof(struct audio_buf_desc_s);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: apb_free
+ *
+ * Free's a previously allocated or referenced Audio Pipeline Buffer
+ *
+ ****************************************************************************/
+
+void apb_free(FAR struct ap_buffer_s *apb)
+{
+ int refcount;
+
+ /* Perform a reference count decrement and possibly release the memory */
+
+ apb_semtake(apb);
+ refcount = apb->crefs--;
+ apb_semgive(apb);
+
+ if (refcount <= 1) {
+ audvdbg("Freeing %p\n", apb);
+ lib_ufree(apb);
+ }
+}
+
+/****************************************************************************
+ * Name: apb_reference
+ *
+ * Claim a reference to an Audio Pipeline Buffer. Each call to apb_reference
+ * will increment the reference count and must have a matching apb_free
+ * call. When the refcount decrements to zero, the buffer will be freed.
+ *
+ ****************************************************************************/
+
+void apb_reference(FAR struct ap_buffer_s *apb)
+{
+ /* Do we need any thread protection here? Almost certaily... */
+
+ apb_semtake(apb);
+ apb->crefs++;
+ apb_semgive(apb);
+}
+
+#endif /* CONFIG_AUDIO */
TINYARALIBS += $(LIBRARIES_DIR)$(DELIM)libfs$(LIBEXT) $(LIBRARIES_DIR)$(DELIM)libdrivers$(LIBEXT)
endif
+# Add libraries for the Audio sub-system
+
+ifeq ($(CONFIG_AUDIO),y)
+TINYARALIBS += $(LIBRARIES_DIR)$(DELIM)libaudio$(LIBEXT)
+endif
+
# Add library for Framework
TINYARALIBS += $(LIBRARIES_DIR)$(DELIM)libframework$(LIBEXT)
source net/Kconfig
endmenu
+menu "Audio Support"
+source audio/Kconfig
+endmenu
+
menu "File Systems"
source fs/Kconfig
endmenu
$(LIBRARIES_DIR)$(DELIM)libfs$(LIBEXT): fs$(DELIM)libfs$(LIBEXT)
$(Q) install fs$(DELIM)libfs$(LIBEXT) $(LIBRARIES_DIR)$(DELIM)libfs$(LIBEXT)
+audio$(DELIM)libaudio$(LIBEXT): context
+ $(Q) $(MAKE) -C audio TOPDIR="$(TOPDIR)" libaudio$(LIBEXT) KERNEL=y EXTRADEFINES=$(KDEFINE)
+
+$(LIBRARIES_DIR)$(DELIM)libaudio$(LIBEXT): audio$(DELIM)libaudio$(LIBEXT)
+ $(Q) install audio$(DELIM)libaudio$(LIBEXT) $(LIBRARIES_DIR)$(DELIM)libaudio$(LIBEXT)
+
drivers$(DELIM)libdrivers$(LIBEXT): context
$(Q) $(MAKE) -C drivers TOPDIR="$(TOPDIR)" libdrivers$(LIBEXT) KERNEL=y EXTRADEFINES=$(KDEFINE)
--- /dev/null
+#
+# For a description of the syntax of this configuration file,
+# see kconfig-language at https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt
+#
+
+config AUDIO
+ bool "Audio Support"
+ default n
+ ---help---
+ Enables overall support for Audio library.
+
+if AUDIO
+
+config AUDIO_MULTI_SESSION
+ bool "Support multiple sessions"
+ default n
+ ---help---
+ Some audio devices, such as USB attached sound cards, may support more
+ than one streaming session at a time (each with one or more audio channels).
+ Selecting this feature adds support for tracking multiple concurrent
+ sessions with the lower-level audio devices.
+
+menu "Audio Buffer Configuration"
+
+config AUDIO_LARGE_BUFFERS
+ bool "Support Audio Buffers with greater than 65K samples"
+ default n
+ ---help---
+ By default, the Audio Pipeline Buffers use a 16-bit max sample count, limiting
+ the number of samples per buffer to 65K. Enable this option to specify a
+ 32-bit max sample count for increased samples / buffer capability.
+ channel capability.
+
+config AUDIO_NUM_BUFFERS
+ int "Number of buffers for audio processing"
+ default 2
+ ---help---
+ Specifies the number of buffers to allocate for audio processing.
+ If Driver Specified buffer sizes is enabled (below), then the
+ low-level drivers will have the opportunity to override this
+ value.
+
+config AUDIO_BUFFER_NUMBYTES
+ int "Size of each audio buffer for audio processing"
+ default 8192
+ ---help---
+ Specifies the allocation size for each audio buffer
+ If Driver Specified buffer sizes is enabled (below), then the
+ low-level drivers will have the opportunity to override this
+ value.
+
+config AUDIO_DRIVER_SPECIFIC_BUFFERS
+ bool "Support for Driver specified buffer sizes"
+ default n
+ ---help---
+ By default, the Audio system uses the same size and number of buffers
+ regardless of the specific audio device in use. Specifying 'y' here
+ adds extra code which allows the lower-level audio device to specify
+ a partucular size and number of buffers.
+
+endmenu # Audio Buffer Configuration
+
+config DEBUG_AUDIO_ERROR
+ bool "Output AUDIO Error Debug Messages"
+ default n
+
+config DEBUG_AUDIO_WARN
+ bool "Output AUDIO Warning Debug Messages"
+ default n
+
+config DEBUG_AUDIO_INFO
+ bool "Output AUDIO Info Debug Messages"
+ default n
+
+
+menu "Supported Audio Formats"
+
+config AUDIO_FORMAT_AC3
+ bool "AC3 Format"
+ default n
+ ---help---
+ Build in support for AC3 (Dolby Digital) Audio format.
+
+config AUDIO_FORMAT_DTS
+ bool "DTS Format"
+ default n
+ ---help---
+ Add in support for DTS format.
+
+config AUDIO_FORMAT_PCM
+ bool "PCM Audio"
+ default y
+ ---help---
+ Build in support for PCM Audio format.
+
+config AUDIO_FORMAT_MP3
+ bool "MPEG 3 Layer 1"
+ default y
+ ---help---
+ Build in support for MP3 Audio format.
+
+config AUDIO_FORMAT_MIDI
+ bool "Midi Format"
+ default n
+ ---help---
+ Add in support for MIDI format.
+
+config AUDIO_FORMAT_WMA
+ bool "WMA Format (see copyright notice)"
+ default n
+ ---help---
+ Add in support for Microsoft Windows Media format.
+
+config AUDIO_FORMAT_OGG_VORBIS
+ bool "Ogg Vorbis format"
+ default n
+ ---help---
+ Build in support for the Open Source Ogg Vorbis format.
+
+endmenu
+
+menu "Exclude Specific Audio Features"
+
+config AUDIO_EXCLUDE_VOLUME
+ bool "Exclude volume controls"
+ default n
+ ---help---
+ Exclude building support for changing the playback volume.
+
+config AUDIO_EXCLUDE_BALANCE
+ bool "Exclude balance controls"
+ default n
+ ---help---
+ Exclude building support for changing the balance.
+
+config AUDIO_EXCLUDE_EQUALIZER
+ bool "Exclude equalizer controls"
+ default y
+ ---help---
+ Exclude building support for setting equalization.
+
+config AUDIO_EQUALIZER_NBANDS
+ int "Number of equalizer bands"
+ default 8
+ depends on !AUDIO_EXCLUDE_EQUALIZER
+ ---help---
+ If equalizer support is not excluded, then it will be necessary to
+ provide the (maximum) number of equalization bands to be supported.
+
+config AUDIO_EXCLUDE_TONE
+ bool "Exclude tone (bass and treble) controls"
+ default y if !AUDIO_EXCLUDE_EQUALIZER
+ default n if AUDIO_EXCLUDE_EQUALIZER
+ ---help---
+ Exclude building support for changing the bass and treble. Normally
+ you would not select both tone controls and equalizer support unless
+ your underlying hardware supports both options.
+
+config AUDIO_EXCLUDE_PAUSE_RESUME
+ bool "Exclude pause and resume controls"
+ default n
+ ---help---
+ Exclude building support for pausing and resuming audio files
+ once they are submitted. If the sound system is being used to play
+ short system notification or error type sounds that typicaly only
+ last a second or two, then there is no need (or chance) to pause or
+ resume sound playback once it has started.
+
+config AUDIO_EXCLUDE_STOP
+ bool "Exclude stop playback controls"
+ default n
+ ---help---
+ Exclude building support for stopping audio files once they are
+ submitted. If the sound system is being used to play short sytem
+ notification or error type sounds that typically only last a second
+ or two, then there is no need (or chance) to stop the sound
+ playback once it has started.
+
+config AUDIO_EXCLUDE_FFORWARD
+ bool "Exclude fast forward controls"
+ default n if !AUDIO_EXCLUDE_STOP
+ default y if AUDIO_EXCLUDE_STOP
+ ---help---
+ Exclude building support for fast forwarding through audio files
+ once they are submitted. Selecting this option would only make
+ if the underlying audio decoding logic is capable of sub-sampling
+ in the stream of audio data.
+
+config AUDIO_EXCLUDE_REWIND
+ bool "Exclude rewind controls"
+ default y
+ ---help---
+ Rewind may be supported by some audio devices, but not the typical
+ device that receives a non-seekable, stream of audio buffers.
+
+endmenu
+
+config AUDIO_CUSTOM_DEV_PATH
+ bool "Use custom device path"
+ default n
+ ---help---
+ By default, all audio devices on the target are are registered in the
+ /dev/audio directory. Select this option to change the default location
+ for the device registration.
+
+if AUDIO_CUSTOM_DEV_PATH
+
+config AUDIO_DEV_ROOT
+ bool "Place audio devices in /dev"
+ default n
+ ---help---
+ This option causes all device entries to appear in /dev with all the
+ other device entries. This option generates the smallest code and
+ RAM footprint.
+
+if !AUDIO_DEV_ROOT
+
+config AUDIO_DEV_PATH
+ string "Base path for Audio devices"
+ default "/dev/audio"
+ ---help---
+ The path on the target where audio devices are registered. The default
+ is to place all audio devices in the /dev/audio/ directory.
+
+endif
+endif
+
+
+# These are here as placeholders of what could be added
+
+if AUDIO_PLANNED
+
+config AUDIO_MIXER
+ bool "Planned - Enable support for the software based Audio Mixer"
+ default n
+ ---help---
+ The Audio mixer is a software-only based component that can be used
+ to perform audio channel or device mixing.
+
+config AUDIO_MIDI_SYNTH
+ bool "Planned - Enable support for the software-based MIDI synthisizer"
+ default n
+ ---help---
+ Builds a simple MIDI synthisizer.
+
+config AUDIO_OUTPUT_JACK_CONTROL
+ bool "Planned - Enable support for output jack control"
+ default n
+ ---help---
+ Builds a simple MIDI synthisizer.
+
+config AUDIO_FONT
+ bool "Planned - Enable support for the Audio Font"
+ default n
+ ---help---
+ The Audio font provides common audio symbols.
+
+endif
+
+endif
--- /dev/null
+###########################################################################
+#
+# Copyright 2017 Samsung Electronics All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+# either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+#
+###########################################################################
+############################################################################
+# audio/Makefile
+#
+# Copyright (C) 2013 Ken Pettit. All rights reserved.
+# Author: Ken Pettit <pettitkd@gmail.com>
+#
+# 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.
+#
+############################################################################
+
+-include $(TOPDIR)/Make.defs
+DELIM ?= $(strip /)
+
+ifeq ($(WINTOOL),y)
+INCDIROPT = -w
+endif
+
+DEPPATH = --dep-path .
+ASRCS =
+CSRCS = audio.c
+VPATH = .
+
+# 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
+
+ifeq ($(CONFIG_AUDIO_FORMAT_PCM),y)
+ CSRCS += pcm_decode.c
+endif
+
+AOBJS = $(ASRCS:.S=$(OBJEXT))
+COBJS = $(CSRCS:.c=$(OBJEXT))
+
+SRCS = $(ASRCS) $(CSRCS)
+OBJS = $(AOBJS) $(COBJS)
+
+BIN = libaudio$(LIBEXT)
+
+all: $(BIN)
+.PHONY: depend clean distclean
+
+$(AOBJS): %$(OBJEXT): %.S
+ $(call ASSEMBLE, $<, $@)
+
+$(COBJS): %$(OBJEXT): %.c
+ $(call COMPILE, $<, $@)
+
+$(BIN): $(OBJS)
+ $(call ARCHIVE, $@, $(OBJS))
+
+.depend: Makefile $(SRCS)
+ $(Q) $(MKDEP) $(DEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep
+ $(Q) touch $@
+
+depend: .depend
+
+clean:
+ $(call DELFILE, $(BIN))
+ $(call CLEAN)
+
+distclean: clean
+ $(call DELFILE, Make.dep)
+ $(call DELFILE, .depend)
+
+-include Make.dep
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * audio/audio.c
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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 <tinyara/config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <tinyara/kmalloc.h>
+#include <tinyara/fs/fs.h>
+#include <tinyara/arch.h>
+#include <tinyara/audio/audio.h>
+#include <mqueue.h>
+
+#include <arch/irq.h>
+
+#ifdef CONFIG_AUDIO
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Debug ********************************************************************/
+/* Non-standard debug that may be enabled just for testing Audio */
+
+#ifndef AUDIO_MAX_DEVICE_PATH
+#define AUDIO_MAX_DEVICE_PATH 32
+#endif
+
+#ifndef CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO
+#define CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO 1
+#endif
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+/* This structure describes the state of the upper half driver */
+
+struct audio_upperhalf_s {
+ uint8_t crefs; /* The number of times the device has been opened */
+ volatile bool started; /* True: playback is active */
+ sem_t exclsem; /* Supports mutual exclusion */
+ FAR struct audio_lowerhalf_s *dev; /* lower-half state */
+ mqd_t usermq; /* User mode app's message queue */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int audio_open(FAR struct file *filep);
+static int audio_close(FAR struct file *filep);
+static ssize_t audio_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
+static ssize_t audio_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int audio_start(FAR struct audio_upperhalf_s *upper, FAR void *session);
+static void audio_callback(FAR void *priv, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status, FAR void *session);
+#else
+static int audio_start(FAR struct audio_upperhalf_s *upper);
+static void audio_callback(FAR void *priv, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status);
+#endif /* CONFIG_AUDIO_MULTI_SESSION */
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_audioops = {
+ audio_open, /* open */
+ audio_close, /* close */
+ audio_read, /* read */
+ audio_write, /* write */
+ 0, /* seek */
+ audio_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , 0 /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/************************************************************************************
+ * Name: audio_open
+ *
+ * Description:
+ * This function is called whenever the Audio device is opened.
+ *
+ ************************************************************************************/
+
+static int audio_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct audio_upperhalf_s *upper = inode->i_private;
+ uint8_t tmp;
+ int ret;
+
+ audinfo("crefs: %d\n", upper->crefs);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0) {
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Increment the count of references to the device. If this the first
+ * time that the driver has been opened for this device, then initialize
+ * the device.
+ */
+
+ tmp = upper->crefs + 1;
+ if (tmp == 0) {
+ /* More than 255 opens; uint8_t overflows to zero */
+
+ ret = -EMFILE;
+ goto errout_with_sem;
+ }
+
+ /* Save the new open count on success */
+
+ upper->crefs = tmp;
+ upper->usermq = NULL;
+ ret = OK;
+
+errout_with_sem:
+ sem_post(&upper->exclsem);
+
+errout:
+ return ret;
+}
+
+/************************************************************************************
+ * Name: audio_close
+ *
+ * Description:
+ * This function is called when the Audio device is closed.
+ *
+ ************************************************************************************/
+
+static int audio_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct audio_upperhalf_s *upper = inode->i_private;
+ int ret;
+
+ audinfo("crefs: %d\n", upper->crefs);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0) {
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Decrement the references to the driver. If the reference count will
+ * decrement to 0, then uninitialize the driver.
+ */
+
+ if (upper->crefs > 1) {
+ upper->crefs--;
+ } else {
+ FAR struct audio_lowerhalf_s *lower = upper->dev;
+
+ /* There are no more references to the port */
+
+ upper->crefs = 0;
+
+ /* Disable the Audio device */
+
+ DEBUGASSERT(lower->ops->shutdown != NULL);
+ audinfo("calling shutdown: %d\n");
+
+ lower->ops->shutdown(lower);
+ }
+ ret = OK;
+
+//errout_with_sem:
+ sem_post(&upper->exclsem);
+
+errout:
+ return ret;
+}
+
+/************************************************************************************
+ * Name: audio_read
+ *
+ * Description:
+ * A dummy read method. This is provided only to satsify the VFS layer.
+ *
+ ************************************************************************************/
+
+static ssize_t audio_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct audio_upperhalf_s *upper = inode->i_private;
+ FAR struct audio_lowerhalf_s *lower = upper->dev;
+
+ /* TODO: Should we check permissions here? */
+
+ /* Audio read operations get passed directly to the lower-level */
+
+ if (lower->ops->read != NULL) {
+ return lower->ops->read(lower, buffer, buflen);
+ }
+
+ return 0;
+}
+
+/************************************************************************************
+ * Name: audio_write
+ *
+ * Description:
+ * A dummy write method. This is provided only to satsify the VFS layer.
+ *
+ ************************************************************************************/
+
+static ssize_t audio_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct audio_upperhalf_s *upper = inode->i_private;
+ FAR struct audio_lowerhalf_s *lower = upper->dev;
+
+ /* TODO: Should we check permissions here? */
+
+ /* Audio write operations get passed directly to the lower-level */
+
+ if (lower->ops->write != NULL) {
+ return lower->ops->write(lower, buffer, buflen);
+ }
+
+ return 0;
+}
+
+/************************************************************************************
+ * Name: audio_start
+ *
+ * Description:
+ * Handle the AUDIOIOC_START ioctl command
+ *
+ ************************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int audio_start(FAR struct audio_upperhalf_s *upper, FAR void *session)
+#else
+static int audio_start(FAR struct audio_upperhalf_s *upper)
+#endif
+{
+ FAR struct audio_lowerhalf_s *lower = upper->dev;
+ int ret = OK;
+
+ DEBUGASSERT(upper != NULL && lower->ops->start != NULL);
+
+ /* Verify that the Audio is not already running */
+
+ if (!upper->started) {
+ /* Invoke the bottom half method to start the audio stream */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->start(lower, session);
+#else
+ ret = lower->ops->start(lower);
+#endif
+
+ /* A return value of zero means that the audio stream was started
+ * successfully.
+ */
+
+ if (ret == OK) {
+ /* Indicate that the audio stream has started */
+
+ upper->started = true;
+ }
+ }
+
+ return ret;
+}
+
+/************************************************************************************
+ * Name: audio_ioctl
+ *
+ * Description:
+ * The standard ioctl method. This is where ALL of the Audio work is done.
+ *
+ ************************************************************************************/
+
+static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct audio_upperhalf_s *upper = inode->i_private;
+ FAR struct audio_lowerhalf_s *lower = upper->dev;
+ FAR struct audio_buf_desc_s *bufdesc;
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ FAR void *session;
+#endif
+ int ret;
+
+ audinfo("cmd: %d arg: %ld\n", cmd, arg);
+
+ /* Get exclusive access to the device structures */
+
+ ret = sem_wait(&upper->exclsem);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Handle built-in ioctl commands */
+
+ switch (cmd) {
+ /* AUDIOIOC_GETCAPS - Get the audio device capabilities.
+ *
+ * ioctl argument: A pointer to the audio_caps_s structure.
+ */
+
+ case AUDIOIOC_GETCAPS: {
+ FAR struct audio_caps_s *caps = (FAR struct audio_caps_s *)((uintptr_t) arg);
+ DEBUGASSERT(lower->ops->getcaps != NULL);
+
+ audinfo("AUDIOIOC_GETCAPS: Device=%d\n", caps->ac_type);
+
+ /* Call the lower-half driver capabilities handler */
+
+ ret = lower->ops->getcaps(lower, caps->ac_type, caps);
+ }
+ break;
+
+ case AUDIOIOC_CONFIGURE: {
+ FAR const struct audio_caps_desc_s *caps = (FAR const struct audio_caps_desc_s *)((uintptr_t) arg);
+ DEBUGASSERT(lower->ops->configure != NULL);
+
+ audinfo("AUDIOIOC_INITIALIZE: Device=%d\n", caps->caps.ac_type);
+
+ /* Call the lower-half driver configure handler */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->configure(lower, caps->session, &caps->caps);
+#else
+ ret = lower->ops->configure(lower, &caps->caps);
+#endif
+ }
+ break;
+
+ case AUDIOIOC_SHUTDOWN: {
+ DEBUGASSERT(lower->ops->shutdown != NULL);
+
+ audinfo("AUDIOIOC_SHUTDOWN\n");
+
+ /* Call the lower-half driver initialize handler */
+ ret = lower->ops->shutdown(lower);
+ }
+ break;
+
+ /* AUDIOIOC_START - Start the audio stream. The AUDIOIOC_SETCHARACTERISTICS
+ * command must have previously been sent.
+ *
+ * ioctl argument: Audio session
+ */
+
+ case AUDIOIOC_START: {
+ audinfo("AUDIOIOC_START\n");
+ DEBUGASSERT(lower->ops->start != NULL);
+
+ /* Start the audio stream */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ session = (FAR void *)arg;
+ ret = audio_start(upper, session);
+#else
+ ret = audio_start(upper);
+#endif
+ }
+ break;
+
+ /* AUDIOIOC_STOP - Stop the audio stream.
+ *
+ * ioctl argument: Audio session
+ */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+ case AUDIOIOC_STOP: {
+ audinfo("AUDIOIOC_STOP\n");
+ DEBUGASSERT(lower->ops->stop != NULL);
+
+ if (upper->started) {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ session = (FAR void *)arg;
+ ret = lower->ops->stop(lower, session);
+#else
+ ret = lower->ops->stop(lower);
+#endif
+ upper->started = false;
+ }
+ }
+ break;
+#endif /* CONFIG_AUDIO_EXCLUDE_STOP */
+
+ /* AUDIOIOC_PAUSE - Pause the audio stream.
+ *
+ * ioctl argument: Audio session
+ */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+
+ case AUDIOIOC_PAUSE: {
+ audinfo("AUDIOIOC_PAUSE\n");
+ DEBUGASSERT(lower->ops->pause != NULL);
+
+ if (upper->started) {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ session = (FAR void *)arg;
+ ret = lower->ops->pause(lower, session);
+#else
+ ret = lower->ops->pause(lower);
+#endif
+ }
+ }
+ break;
+
+ /* AUDIOIOC_RESUME - Resume the audio stream.
+ *
+ * ioctl argument: Audio session
+ */
+
+ case AUDIOIOC_RESUME: {
+ audinfo("AUDIOIOC_RESUME\n");
+ DEBUGASSERT(lower->ops->resume != NULL);
+
+ if (upper->started) {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ session = (FAR void *)arg;
+ ret = lower->ops->resume(lower, session);
+#else
+ ret = lower->ops->resume(lower);
+#endif
+ }
+ }
+ break;
+
+#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
+
+ /* AUDIOIOC_ALLOCBUFFER - Allocate an audio buffer
+ *
+ * ioctl argument: pointer to an audio_buf_desc_s structure
+ */
+
+ case AUDIOIOC_ALLOCBUFFER: {
+ audinfo("AUDIOIOC_ALLOCBUFFER\n");
+
+ bufdesc = (FAR struct audio_buf_desc_s *)arg;
+ if (lower->ops->allocbuffer) {
+ ret = lower->ops->allocbuffer(lower, bufdesc);
+ } else {
+ /* Perform a simple kumm_malloc operation assuming 1 session */
+
+ ret = apb_alloc(bufdesc);
+ }
+ }
+ break;
+
+ /* AUDIOIOC_FREEBUFFER - Free an audio buffer
+ *
+ * ioctl argument: pointer to an audio_buf_desc_s structure
+ */
+
+ case AUDIOIOC_FREEBUFFER: {
+ audinfo("AUDIOIOC_FREEBUFFER\n");
+
+ bufdesc = (FAR struct audio_buf_desc_s *)arg;
+ if (lower->ops->freebuffer) {
+ ret = lower->ops->freebuffer(lower, bufdesc);
+ } else {
+ /* Perform a simple apb_free operation */
+
+ DEBUGASSERT(bufdesc->u.pBuffer != NULL);
+ apb_free(bufdesc->u.pBuffer);
+ ret = sizeof(struct audio_buf_desc_s);
+ }
+ }
+ break;
+
+ /* AUDIOIOC_ENQUEUEBUFFER - Enqueue an audio buffer
+ *
+ * ioctl argument: pointer to an audio_buf_desc_s structure
+ */
+
+ case AUDIOIOC_ENQUEUEBUFFER: {
+ audinfo("AUDIOIOC_ENQUEUEBUFFER\n");
+
+ DEBUGASSERT(lower->ops->enqueuebuffer != NULL);
+
+ bufdesc = (FAR struct audio_buf_desc_s *)arg;
+ ret = lower->ops->enqueuebuffer(lower, bufdesc->u.pBuffer);
+ }
+ break;
+
+ /* AUDIOIOC_REGISTERMQ - Register a client Message Queue
+ *
+ * TODO: This needs to have multi session support.
+ */
+
+ case AUDIOIOC_REGISTERMQ: {
+ audinfo("AUDIOIOC_REGISTERMQ\n");
+
+ upper->usermq = (mqd_t) arg;
+ ret = OK;
+ }
+ break;
+
+ /* AUDIOIOC_UNREGISTERMQ - Register a client Message Queue
+ *
+ * TODO: This needs to have multi session support.
+ */
+
+ case AUDIOIOC_UNREGISTERMQ: {
+ audinfo("AUDIOIOC_UNREGISTERMQ\n");
+
+ upper->usermq = NULL;
+ ret = OK;
+ }
+ break;
+
+ /* AUDIOIOC_RESERVE - Reserve a session with the driver
+ *
+ * ioctl argument - pointer to receive the session context
+ */
+
+ case AUDIOIOC_RESERVE: {
+ audinfo("AUDIOIOC_RESERVE\n");
+ DEBUGASSERT(lower->ops->reserve != NULL);
+
+ /* Call lower-half to perform the reservation */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->reserve(lower, (FAR void **)arg);
+#else
+ ret = lower->ops->reserve(lower);
+#endif
+ }
+ break;
+
+ /* AUDIOIOC_RESERVE - Reserve a session with the driver
+ *
+ * ioctl argument - pointer to receive the session context
+ */
+
+ case AUDIOIOC_RELEASE: {
+ audinfo("AUDIOIOC_RELEASE\n");
+ DEBUGASSERT(lower->ops->release != NULL);
+
+ /* Call lower-half to perform the release */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->release(lower, (FAR void *)arg);
+#else
+ ret = lower->ops->release(lower);
+#endif
+ }
+ break;
+
+ /* Any unrecognized IOCTL commands might be platform-specific ioctl commands */
+
+ default: {
+ audinfo("Forwarding unrecognized cmd: %d arg: %ld\n", cmd, arg);
+ DEBUGASSERT(lower->ops->ioctl != NULL);
+ ret = lower->ops->ioctl(lower, cmd, arg);
+ }
+ break;
+ }
+
+ sem_post(&upper->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: audio_dequeuebuffer
+ *
+ * Description:
+ * Dequeues a previously enqueued Audio Pipeline Buffer.
+ *
+ * 1. The upper half driver calls the enqueuebuffer method, providing the
+ * lower half driver with the ab_buffer to process.
+ * 2. The lower half driver's enqueuebuffer will either processes the
+ * buffer directly, or more likely add it to a queue for processing
+ * by a background thread or worker task.
+ * 3. When the lower half driver has completed processing of the enqueued
+ * ab_buffer, it will call this routine to indicate processing of the
+ * buffer is complete.
+ * 4. When this routine is called, it will check if any threads are waiting
+ * to enqueue additional buffers and "wake them up" for further
+ * processing.
+ *
+ * Input parameters:
+ * handle - This is the handle that was provided to the lower-half
+ * start() method.
+ * apb - A pointer to the previsously enqueued ap_buffer_s
+ * status - Status of the dequeue operation
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static inline void audio_dequeuebuffer(FAR struct audio_upperhalf_s *upper, FAR struct ap_buffer_s *apb, uint16_t status, FAR void *session)
+#else
+static inline void audio_dequeuebuffer(FAR struct audio_upperhalf_s *upper, FAR struct ap_buffer_s *apb, uint16_t status)
+#endif
+{
+ struct audio_msg_s msg;
+
+ audinfo("Entry\n");
+
+ /* Send a dequeue message to the user if a message queue is registered */
+
+ if (upper->usermq != NULL) {
+ msg.msgId = AUDIO_MSG_DEQUEUE;
+ msg.u.pPtr = apb;
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ msg.session = session;
+#endif
+ apb->flags |= AUDIO_APB_DEQUEUED;
+ mq_send(upper->usermq, (FAR const char *)&msg, sizeof(msg), CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO);
+ }
+}
+
+/****************************************************************************
+ * Name: audio_complete
+ *
+ * Description:
+ * Send an AUDIO_MSG_COMPLETE message to the client to indicate that the
+ * active playback has completed. The lower-half driver initiates this
+ * call via its callback pointer to our upper-half driver.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static inline void audio_complete(FAR struct audio_upperhalf_s *upper, FAR struct ap_buffer_s *apb, uint16_t status, FAR void *session)
+#else
+static inline void audio_complete(FAR struct audio_upperhalf_s *upper, FAR struct ap_buffer_s *apb, uint16_t status)
+#endif
+{
+ struct audio_msg_s msg;
+
+ audinfo("Entry\n");
+
+ /* Send a dequeue message to the user if a message queue is registered */
+
+ upper->started = false;
+ if (upper->usermq != NULL) {
+ msg.msgId = AUDIO_MSG_COMPLETE;
+ msg.u.pPtr = NULL;
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ msg.session = session;
+#endif
+ mq_send(upper->usermq, (FAR const char *)&msg, sizeof(msg), CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO);
+ }
+}
+
+/****************************************************************************
+ * Name: audio_callback
+ *
+ * Description:
+ * Provides a callback interface for lower-half drivers to call to the
+ * upper-half for buffer dequeueing, error reporting, etc.
+ *
+ * Input parameters:
+ * priv - Private context data owned by the upper-half
+ * reason - The reason code for the callback
+ * apb - A pointer to the previsously enqueued ap_buffer_s
+ * status - Status information associated with the callback
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static void audio_callback(FAR void *handle, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status, FAR void *session)
+#else
+static void audio_callback(FAR void *handle, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status)
+#endif
+{
+ FAR struct audio_upperhalf_s *upper = (FAR struct audio_upperhalf_s *)handle;
+
+ audinfo("Entry\n");
+
+ /* Perform operation based on reason code */
+
+ switch (reason) {
+ case AUDIO_CALLBACK_DEQUEUE: {
+ /* Call the dequeue routine */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ audio_dequeuebuffer(upper, apb, status, session);
+#else
+ audio_dequeuebuffer(upper, apb, status);
+#endif
+ break;
+ }
+
+ /* Lower-half I/O error occurred */
+
+ case AUDIO_CALLBACK_IOERR: {
+ }
+ break;
+
+ /* Lower-half driver has completed a playback */
+
+ case AUDIO_CALLBACK_COMPLETE: {
+ /* Send a complete message to the user if a message queue is registered */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ audio_complete(upper, apb, status, session);
+#else
+ audio_complete(upper, apb, status);
+#endif
+ }
+ break;
+
+ default: {
+ auddbg("ERROR: Unknown callback reason code %d\n", reason);
+ break;
+ }
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: audio_register
+ *
+ * Description:
+ * This function binds an instance of a "lower half" audio driver with the
+ * "upper half" Audio device and registers that device so that can be used
+ * by application code.
+ *
+ * When this function is called, the "lower half" driver should be in the
+ * reset state (as if the shutdown() method had already been called).
+ *
+ * Input parameters:
+ * path - The full path to the driver to be registers in the NuttX pseudo-
+ * filesystem. The recommended convention is to name Audio drivers
+ * based on the function they provide, such as "/dev/pcm0", "/dev/mp31",
+ * etc.
+ * dev - A pointer to an instance of lower half audio driver. This instance
+ * is bound to the Audio driver and must persists as long as the driver
+ * persists.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int audio_register(FAR const char *name, FAR struct audio_lowerhalf_s *dev)
+{
+ FAR struct audio_upperhalf_s *upper;
+ char path[AUDIO_MAX_DEVICE_PATH];
+ static bool dev_audio_created = false;
+#ifndef CONFIG_AUDIO_CUSTOM_DEV_PATH
+ FAR const char *devname = "/dev/audio";
+#elif !defined(CONFIG_AUDIO_DEV_ROOT)
+ FAR const char *devname = CONFIG_AUDIO_DEV_PATH;
+ FAR const char *ptr;
+ FAR char *pathptr;
+#endif
+
+ /* Allocate the upper-half data structure */
+
+ upper = (FAR struct audio_upperhalf_s *)kmm_zalloc(sizeof(struct audio_upperhalf_s));
+ if (!upper) {
+ auddbg("ERROR: Allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the Audio device structure (it was already zeroed by kmm_zalloc()) */
+
+ sem_init(&upper->exclsem, 0, 1);
+ upper->dev = dev;
+
+#ifdef CONFIG_AUDIO_CUSTOM_DEV_PATH
+
+#ifdef CONFIG_AUDIO_DEV_ROOT
+
+ /* This is the simple case ... No need to make a directory */
+
+ strcpy(path, "/dev/");
+ strcat(path, name);
+
+#else
+ /* Ensure the path begins with "/dev" as we don't support placing device
+ * anywhere but in the /dev directory
+ */
+
+ DEBUGASSERT(strncmp(devname, "/dev", 4) == 0);
+
+ /* Create a /dev/audio directory. */
+
+ if (!dev_audio_created) {
+ /* Get path name after "/dev" */
+
+ ptr = &devname[4];
+ if (*ptr == '/') {
+ ptr++;
+ }
+
+ strcpy(path, "/dev/");
+ pathptr = &path[5];
+
+ /* Do mkdir for each segment of the path */
+
+ while (*ptr != '\0') {
+ /* Build next path segment into path variable */
+
+ while (*ptr != '/' && *ptr != '\0') {
+ *pathptr++ = *ptr++;
+ }
+ *pathptr = '\0';
+
+ /* Make this level of directory */
+
+ mkdir(path, 0644);
+
+ /* Check for another level */
+
+ *pathptr++ = '/';
+ if (*ptr == '/') {
+ ptr++;
+ }
+ }
+
+ /* Indicate we have created the audio dev path */
+
+ dev_audio_created = true;
+ }
+
+ /* Now build the path for registration */
+
+ strcpy(path, devname);
+ if (devname[sizeof(devname) - 1] != '/') {
+ strcat(path, "/");
+ }
+
+ strcat(path, name);
+
+#endif /* CONFIG_AUDIO_DEV_PATH=="/dev" */
+
+#else /* CONFIG_AUDIO_CUSTOM_DEV_PATH */
+
+ /* Create a /dev/audio directory. */
+
+ if (!dev_audio_created) {
+ /* We don't check for error here because even if it fails, then
+ * the register_driver call below will return an error condition
+ * for us.
+ */
+
+ mkdir(devname, 0644);
+ dev_audio_created = true;
+ }
+
+ /* Register the Audio device */
+
+ memset(path, 0, AUDIO_MAX_DEVICE_PATH);
+ strcpy(path, devname);
+ strcat(path, "/");
+ strncat(path, name, AUDIO_MAX_DEVICE_PATH - 11);
+#endif
+
+ /* Give the lower-half a context to the upper half */
+
+ dev->upper = audio_callback;
+ dev->priv = upper;
+
+ audinfo("Registering %s\n", path);
+ return register_driver(path, &g_audioops, 0666, upper);
+}
+
+#endif /* CONFIG_AUDIO */
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * audio/pcm_decode.c
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@tinyara.org>
+ *
+ * Based on the original audio framework from:
+ *
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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 <tinyara/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <tinyara/kmalloc.h>
+#include <tinyara/audio/audio.h>
+#include <tinyara/audio/pcm.h>
+
+#if defined(CONFIG_AUDIO) && defined(CONFIG_AUDIO_FORMAT_PCM)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+
+#define CONFIG_PCM_DEBUG 1 /* For now */
+
+/* Often defined and re-defined macros */
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* This structure describes the internal state of the PCM decoder */
+
+struct pcm_decode_s {
+ /* This is is our 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 pcm_decode_s.
+ */
+
+ struct audio_lowerhalf_s export;
+
+ /* These are our operations that intervene between the player application
+ * and the lower level driver. Unlike the ops in the struct
+ * audio_lowerhalf_s, these are writeable because we need to customize a
+ * few of the methods based upon what is supported by the the lower level
+ * driver.
+ */
+
+ struct audio_ops_s ops;
+
+ /* This is the contained, low-level DAC-type device and will receive the
+ * decoded PCM audio data.
+ */
+
+ FAR struct audio_lowerhalf_s *lower;
+
+ /* Session returned from the lower level driver */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ FAR void *session;
+#endif
+
+ /* These are values extracted from WAV file header */
+
+ uint32_t samprate; /* 8000, 44100, ... */
+ uint32_t byterate; /* samprate * nchannels * bpsamp / 8 */
+ uint8_t align; /* nchannels * bpsamp / 8 */
+ uint8_t bpsamp; /* Bits per sample: 8 bits = 8, 16 bits = 16 */
+ uint8_t nchannels; /* Mono=1, Stereo=2 */
+ bool streaming; /* Streaming PCM data chunk */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+ /* Fast forward support */
+
+ uint8_t subsample; /* Fast forward rate: See AUDIO_SUBSAMPLE_* defns */
+ uint8_t skip; /* Number of sample bytes to be skipped */
+ uint8_t npartial; /* Size of the partially copied sample */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Helper functions *********************************************************/
+
+#ifdef CONFIG_PCM_DEBUG
+static void pcm_dump(FAR const struct wav_header_s *wav);
+#else
+#define pcm_dump(w)
+#endif
+
+#ifdef CONFIG_ENDIAN_BIG
+static uint16_t pcm_leuint16(uint16_t value);
+static uint16_t pcm_leuint32(uint32_t value);
+#else
+#define pcm_leuint16(v) (v)
+#define pcm_leuint32(v) (v)
+#endif
+
+static inline bool pcm_validwav(FAR const struct wav_header_s *wav);
+static bool pcm_parsewav(FAR struct pcm_decode_s *priv, uint8_t *data);
+
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+static void pcm_subsample_configure(FAR struct pcm_decode_s *priv, uint8_t subsample);
+static void pcm_subsample(FAR struct pcm_decode_s *priv, FAR struct ap_buffer_s *apb);
+#endif
+
+/* struct audio_lowerhalf_s methods *****************************************/
+
+static int pcm_getcaps(FAR struct audio_lowerhalf_s *dev, int type, FAR struct audio_caps_s *caps);
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_configure(FAR struct audio_lowerhalf_s *dev, FAR void *session, FAR const struct audio_caps_s *caps);
+#else
+static int pcm_configure(FAR struct audio_lowerhalf_s *dev, FAR const struct audio_caps_s *caps);
+#endif
+
+static int pcm_shutdown(FAR struct audio_lowerhalf_s *dev);
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_start(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+static int pcm_start(FAR struct audio_lowerhalf_s *dev);
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+static int pcm_stop(FAR struct audio_lowerhalf_s *dev);
+#endif
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+static int pcm_pause(FAR struct audio_lowerhalf_s *dev);
+#endif
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+static int pcm_resume(FAR struct audio_lowerhalf_s *dev);
+#endif
+#endif
+
+static int pcm_allocbuffer(FAR struct audio_lowerhalf_s *dev, FAR struct audio_buf_desc_s *apb);
+static int pcm_freebuffer(FAR struct audio_lowerhalf_s *dev, FAR struct audio_buf_desc_s *apb);
+static int pcm_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb);
+static int pcm_cancelbuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb);
+static int pcm_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, unsigned long arg);
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_reserve(FAR struct audio_lowerhalf_s *dev, FAR void **session);
+#else
+static int pcm_reserve(FAR struct audio_lowerhalf_s *dev);
+#endif
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_release(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+static int pcm_release(FAR struct audio_lowerhalf_s *dev);
+#endif
+
+/* Audio callback */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static void pcm_callback(FAR void *arg, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status, FAR void *session);
+#else
+static void pcm_callback(FAR void *arg, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pcm_dump
+ *
+ * Description:
+ * Dump a WAV file header.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_PCM_DEBUG
+static void pcm_dump(FAR const struct wav_header_s *wav)
+{
+ audinfo("Wave file header\n");
+ audinfo(" Header Chunk:\n");
+ audinfo(" Chunk ID: 0x%08x\n", wav->hdr.chunkid);
+ audinfo(" Chunk Size: %u\n", wav->hdr.chunklen);
+ audinfo(" Format: 0x%08x\n", wav->hdr.format);
+ audinfo(" Format Chunk:\n");
+ audinfo(" Chunk ID: 0x%08x\n", wav->fmt.chunkid);
+ audinfo(" Chunk Size: %u\n", wav->fmt.chunklen);
+ audinfo(" Audio Format: 0x%04x\n", wav->fmt.format);
+ audinfo(" Num. Channels: %d\n", wav->fmt.nchannels);
+ audinfo(" Sample Rate: %u\n", wav->fmt.samprate);
+ audinfo(" Byte Rate: %u\n", wav->fmt.byterate);
+ audinfo(" Block Align: %d\n", wav->fmt.align);
+ audinfo(" Bits Per Sample: %d\n", wav->fmt.bpsamp);
+ audinfo(" Data Chunk:\n");
+ audinfo(" Chunk ID: 0x%08x\n", wav->data.chunkid);
+ audinfo(" Chunk Size: %u\n", wav->data.chunklen);
+}
+#endif
+
+/****************************************************************************
+ * Name: pcm_leuint16
+ *
+ * Description:
+ * Get a 16-bit value stored in little endian order for a big-endian
+ * machine.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ENDIAN_BIG
+static uint16_t pcm_leuint16(uint16_t value)
+{
+ return (((value & 0x00ff) << 8) | ((value >> 8) & 0x00ff));
+}
+#endif
+
+/****************************************************************************
+ * Name: pcm_leuint16
+ *
+ * Description:
+ * Get a 16-bit value stored in little endian order for a big-endian
+ * machine.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ENDIAN_BIG
+static uint16_t pcm_leuint32(uint32_t value)
+{
+ return (((value & 0x000000ff) << 24) | ((value & 0x0000ff00) << 8) | ((value & 0x00ff0000) >> 8) | ((value & 0xff000000) >> 24));
+}
+#endif
+
+/****************************************************************************
+ * Name: pcm_validwav
+ *
+ * Description:
+ * Return true if this is a valid WAV file header
+ *
+ ****************************************************************************/
+
+static inline bool pcm_validwav(FAR const struct wav_header_s *wav)
+{
+ return (wav->hdr.chunkid == WAV_HDR_CHUNKID && wav->hdr.format == WAV_HDR_FORMAT && wav->fmt.chunkid == WAV_FMT_CHUNKID && wav->fmt.chunklen == WAV_FMT_CHUNKLEN && wav->fmt.format == WAV_FMT_FORMAT && wav->fmt.nchannels < 256 && wav->fmt.align < 256 && wav->fmt.bpsamp < 256 && wav->data.chunkid == WAV_DATA_CHUNKID);
+}
+
+/****************************************************************************
+ * Name: pcm_parsewav
+ *
+ * Description:
+ * Parse and verify the WAV file header. A WAV file is a particular
+ * packaging of an audio file; on PCM encoded WAV files are accepted by
+ * this driver.
+ *
+ ****************************************************************************/
+
+static bool pcm_parsewav(FAR struct pcm_decode_s *priv, uint8_t *data)
+{
+ FAR const struct wav_header_s *wav = (FAR const struct wav_header_s *)data;
+ struct wav_header_s localwav;
+ bool ret;
+
+ /* Transfer the purported WAV file header into our stack storage,
+ * correcting for endian issues as needed.
+ */
+
+ localwav.hdr.chunkid = pcm_leuint32(wav->hdr.chunkid);
+ localwav.hdr.chunklen = pcm_leuint32(wav->hdr.chunklen);
+ localwav.hdr.format = pcm_leuint32(wav->hdr.format);
+
+ localwav.fmt.chunkid = pcm_leuint32(wav->fmt.chunkid);
+ localwav.fmt.chunklen = pcm_leuint32(wav->fmt.chunklen);
+ localwav.fmt.format = pcm_leuint16(wav->fmt.format);
+ localwav.fmt.nchannels = pcm_leuint16(wav->fmt.nchannels);
+ localwav.fmt.samprate = pcm_leuint32(wav->fmt.samprate);
+ localwav.fmt.byterate = pcm_leuint32(wav->fmt.byterate);
+ localwav.fmt.align = pcm_leuint16(wav->fmt.align);
+ localwav.fmt.bpsamp = pcm_leuint16(wav->fmt.bpsamp);
+
+ localwav.data.chunkid = pcm_leuint32(wav->data.chunkid);
+ localwav.data.chunklen = pcm_leuint32(wav->data.chunklen);
+
+ /* Dump the converted wave header information */
+
+ pcm_dump(&localwav);
+
+ /* Check if the file is a valid PCM WAV header */
+
+ ret = pcm_validwav(&localwav);
+ if (ret) {
+ /* Yes... pick off the relevant format values and save then in the
+ * device structure.
+ */
+
+ priv->samprate = localwav.fmt.samprate; /* 8000, 44100, ... */
+ priv->byterate = localwav.fmt.byterate; /* samprate * nchannels * bpsamp / 8 */
+ priv->align = localwav.fmt.align; /* nchannels * bpsamp / 8 */
+ priv->bpsamp = localwav.fmt.bpsamp; /* Bits per sample: 8 bits = 8, 16 bits = 16 */
+ priv->nchannels = localwav.fmt.nchannels; /* Mono=1, Stereo=2 */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+ /* We are going to subsample, there then are some restrictions on the
+ * number of channels and sample sizes that we can handle.
+ */
+
+ if (priv->bpsamp != 8 && priv->bpsamp != 16) {
+ auddbg("ERROR: Cannot support bits per sample of %d in this mode\n", priv->bpsamp);
+ return -EINVAL;
+ }
+
+ if (priv->nchannels != 1 && priv->nchannels != 2) {
+ auddbg("ERROR: Cannot support number of channles of %d in this mode\n", priv->nchannels);
+ return -EINVAL;
+ }
+
+ DEBUGASSERT(priv->align == priv->nchannels * priv->bpsamp / 8);
+#endif
+ }
+
+ /* And return true if the the file is a valid WAV header file */
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: pcm_subsample_configure
+ *
+ * Description:
+ * Configure to perform sub-sampling (or not) on the following audio
+ * buffers.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+static void pcm_subsample_configure(FAR struct pcm_decode_s *priv, uint8_t subsample)
+{
+ audinfo("subsample: %d\n", subsample);
+
+ /* Three possibilities:
+ *
+ * 1. We were playing normally and we have been requested to begin fast
+ * forwarding.
+ */
+
+ if (priv->subsample == AUDIO_SUBSAMPLE_NONE) {
+ /* Ignore request to stop fast forwarding if we are already playing
+ * normally.
+ */
+
+ if (subsample != AUDIO_SUBSAMPLE_NONE) {
+ audinfo("Start sub-sampling\n");
+
+ /* Save the current subsample setting. Subsampling will begin on
+ * then next audio buffer that we receive.
+ */
+
+ priv->npartial = 0;
+ priv->skip = 0;
+ priv->subsample = subsample;
+ }
+ }
+
+ /* 2. We were already fast forwarding and we have been asked to return to
+ * normal play.
+ */
+
+ else if (subsample == AUDIO_SUBSAMPLE_NONE) {
+ audinfo("Stop sub-sampling\n");
+
+ /* Indicate that we are in normal play mode. This will take effect
+ * when the next audio buffer is received.
+ */
+
+ priv->npartial = 0;
+ priv->skip = 0;
+ priv->subsample = AUDIO_SUBSAMPLE_NONE;
+ }
+
+ /* 3. Were already fast forwarding and we have been asked to change the
+ * sub-sampling rate.
+ */
+
+ else if (priv->subsample != subsample) {
+ /* Just save the new subsample setting. It will take effect on the
+ * next audio buffer that we receive.
+ */
+
+ priv->subsample = subsample;
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: pcm_subsample
+ *
+ * Description:
+ * Given a newly received audio buffer, perform sub-sampling in-place in
+ * the audio buffer. Since the sub-sampled data will always be smaller
+ * than the original buffer, no additional buffering should be necessary.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+static void pcm_subsample(FAR struct pcm_decode_s *priv, FAR struct ap_buffer_s *apb)
+{
+ FAR const uint8_t *src;
+ FAR uint8_t *dest;
+ unsigned int destsize;
+ unsigned int srcsize;
+ unsigned int skipsize;
+ unsigned int copysize;
+ unsigned int i;
+
+ /* Are we sub-sampling? */
+
+ if (priv->subsample == AUDIO_SUBSAMPLE_NONE) {
+ /* No.. do nothing to the buffer */
+
+ return;
+ }
+
+ /* Yes.. we will need to subsample the newly received buffer in-place by
+ * copying from the upper end of the buffer to the lower end.
+ */
+
+ src = &apb->samp[apb->curbyte];
+ dest = apb->samp;
+
+ srcsize = apb->nbytes - apb->curbyte;
+ destsize = apb->nmaxbytes;
+
+ /* This is the number of bytes that we need to skip between samples */
+
+ skipsize = priv->align * (priv->subsample - 1);
+
+ /* Let's deal with any partial samples from the last buffer */
+
+ if (priv->npartial > 0) {
+ /* Let's get an impossible corner case out of the way. What if we
+ * received a tiny audio buffer. So small, that it (plus any previous
+ * sample) is smaller than one sample.
+ */
+
+ if (priv->npartial + srcsize < priv->align) {
+ /* Update the partial sample size and return the unmodified
+ * buffer.
+ */
+
+ priv->npartial += srcsize;
+ return;
+ }
+
+ /* We do at least have enough to complete the sample. If this data
+ * does not resides at the correct position at the from of the audio
+ * buffer, then we will need to copy it.
+ */
+
+ copysize = priv->align - priv->npartial;
+ if (apb->curbyte > 0) {
+ /* We have to copy down */
+
+ for (i = 0; i < copysize; i++) {
+ *dest++ = *src++;
+ }
+ } else {
+ /* If the data is already position at the beginning of the audio
+ * buffer, then just increment the buffer pointers around the
+ * data.
+ */
+
+ src += copysize;
+ dest += copysize;
+ }
+
+ /* Update the number of bytes in the working buffer and reset the
+ * skip value
+ */
+
+ priv->npartial = 0;
+ srcsize -= copysize;
+ destsize -= copysize;
+ priv->skip = skipsize;
+ }
+
+ /* Now loop until either the entire audio buffer has been sub-sampling.
+ * This copy in place works because we know that the sub-sampled data
+ * will always be smaller than the original data.
+ */
+
+ while (srcsize > 0) {
+ /* Do we need to skip ahead in the buffer? */
+
+ if (priv->skip > 0) {
+ /* How much can we skip in this buffer? Depends on the smaller
+ * of (1) the number of bytes that we need to skip and (2) the
+ * number of bytes available in the newly received audio buffer.
+ */
+
+ copysize = MIN(priv->skip, srcsize);
+
+ priv->skip -= copysize;
+ src += copysize;
+ srcsize -= copysize;
+ }
+
+ /* Copy the sample from the audio buffer into the working buffer. */
+
+ else {
+ /* Do we have space for the whole sample? */
+
+ if (srcsize < priv->align) {
+ /* No.. this is a partial copy */
+
+ copysize = srcsize;
+ priv->npartial = srcsize;
+ } else {
+ /* Copy the whole sample and re-arm the skip size */
+
+ copysize = priv->align;
+ priv->skip = skipsize;
+ }
+
+ /* Now copy the sample from the end of audio buffer to the beginning. */
+
+ for (i = 0; i < copysize; i++) {
+ *dest++ = *src++;
+ }
+
+ /* Updates bytes available in the source buffer and bytes
+ * remaining in the destination buffer.
+ */
+
+ srcsize -= copysize;
+ destsize -= copysize;
+ }
+ }
+
+ /* Update the size and offset data in the audio buffer */
+
+ apb->curbyte = 0;
+ apb->nbytes = apb->nmaxbytes - destsize;
+}
+#endif
+
+/****************************************************************************
+ * Name: pcm_getcaps
+ *
+ * Description:
+ * This method is called to retrieve the lower-half device capabilities.
+ * It will be called with device type AUDIO_TYPE_QUERY to request the
+ * overall capabilities, such as to determine the types of devices supported
+ * audio formats supported, etc. Then it may be called once or more with
+ * reported supported device types to determine the specific capabilities
+ * of that device type (such as MP3 encoder, WMA encoder, PCM output, etc.).
+ *
+ ****************************************************************************/
+
+static int pcm_getcaps(FAR struct audio_lowerhalf_s *dev, int type, FAR struct audio_caps_s *caps)
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+ int ret;
+
+ DEBUGASSERT(priv);
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->getcaps);
+
+ /* Get the capabilities of the lower-level driver */
+
+ ret = lower->ops->getcaps(lower, type, caps);
+ if (ret < 0) {
+ auddbg("ERROR: Lower getcaps() failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Modify the capabilities reported by the lower driver: PCM is the only
+ * supported format that we will report, regardless of what the lower driver
+ * reported.
+ */
+
+ if (caps->ac_subtype == AUDIO_TYPE_QUERY) {
+ caps->ac_format.hw = (1 << (AUDIO_FMT_PCM - 1));
+ }
+
+ return caps->ac_len;
+}
+
+/****************************************************************************
+ * Name: pcm_configure
+ *
+ * Description:
+ * This method is called to bind the lower-level driver to the upper-level
+ * driver and to configure the driver for a specific mode of
+ * operation defined by the parameters selected in supplied device caps
+ * structure. The lower-level device should perform any initialization
+ * needed to prepare for operations in the specified mode. It should not,
+ * however, process any audio data until the start method is called.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_configure(FAR struct audio_lowerhalf_s *dev, FAR void *session, FAR const struct audio_caps_s *caps)
+#else
+static int pcm_configure(FAR struct audio_lowerhalf_s *dev, FAR const struct audio_caps_s *caps)
+#endif
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+ /* Pick off commands to perform sub-sampling. Those are done by this
+ * decoder. All of configuration settings are handled by the lower level
+ * audio driver.
+ */
+
+ if (caps->ac_type == AUDIO_TYPE_PROCESSING && caps->ac_format.hw == AUDIO_PU_SUBSAMPLE_FORWARD) {
+ /* Configure sub-sampling and return to avoid forwarding the
+ * configuration to the lower level
+ * driver.
+ */
+
+ pcm_subsample_configure(priv, caps->ac_controls.b[0]);
+ return OK;
+ }
+#endif
+
+ /* Defer all other operations to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->configure);
+
+ audinfo("Defer to lower configure\n");
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ return lower->ops->configure(lower, session, caps);
+#else
+ return lower->ops->configure(lower, caps);
+#endif
+}
+
+/****************************************************************************
+ * Name: pcm_shutdown
+ *
+ * Description:
+ * This method is called when the driver is closed. The lower half driver
+ * should stop processing audio data, including terminating any active
+ * output generation. It should also disable the audio hardware and put
+ * it into the lowest possible power usage state.
+ *
+ * Any enqueued Audio Pipeline Buffers that have not been processed / dequeued
+ * should be dequeued by this function.
+ *
+ ****************************************************************************/
+
+static int pcm_shutdown(FAR struct audio_lowerhalf_s *dev)
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* We are no longer streaming audio */
+
+ priv->streaming = false;
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->start);
+
+ audinfo("Defer to lower shutdown\n");
+ return lower->ops->shutdown(lower);
+}
+
+/****************************************************************************
+ * Name: pcm_start
+ *
+ * Description:
+ * Start audio streaming in the configured mode. For input and synthesis
+ * devices, this means it should begin sending streaming audio data. For output
+ * or processing type device, it means it should begin processing of any enqueued
+ * Audio Pipeline Buffers.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_start(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int pcm_start(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->start);
+
+ audinfo("Defer to lower start\n");
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ return lower->ops->start(lower, session);
+#else
+ return lower->ops->start(lower);
+#endif
+}
+
+/****************************************************************************
+ * Name: pcm_stop
+ *
+ * Description:
+ * Stop audio streaming and/or processing of enqueued Audio Pipeline
+ * Buffers
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int pcm_stop(FAR struct audio_lowerhalf_s *dev)
+#endif
+#endif
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* We are no longer streaming */
+
+ priv->streaming = false;
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->stop);
+
+ audinfo("Defer to lower stop\n");
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ return lower->ops->stop(lower, session);
+#else
+ return lower->ops->stop(lower);
+#endif
+}
+
+/****************************************************************************
+ * Name: pcm_pause
+ *
+ * Description:
+ * Pause the audio stream. Should keep current playback context active
+ * in case a resume is issued. Could be called and then followed by a stop.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int pcm_pause(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->pause);
+
+ audinfo("Defer to lower pause\n");
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ return lower->ops->pause(lower, session);
+#else
+ return lower->ops->pause(lower);
+#endif
+}
+
+/****************************************************************************
+ * Name: pcm_resume
+ *
+ * Description:
+ * Resumes audio streaming after a pause.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int pcm_resume(FAR struct audio_lowerhalf_s *dev)
+#endif
+#endif
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->resume);
+
+ audinfo("Defer to lower resume\n");
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ return lower->ops->resume(lower, session);
+#else
+ return lower->ops->resume(lower);
+#endif
+}
+
+/****************************************************************************
+ * Name: pcm_allocbuffer
+ *
+ * Description:
+ * Allocate an audio pipeline buffer. This routine provides the
+ * lower-half driver with the opportunity to perform special buffer
+ * allocation if needed, such as allocating from a specific memory
+ * region (DMA-able, etc.). If not supplied, then the top-half
+ * driver will perform a standard kumm_malloc using normal user-space
+ * memory region.
+ *
+ ****************************************************************************/
+
+static int pcm_allocbuffer(FAR struct audio_lowerhalf_s *dev, FAR struct audio_buf_desc_s *apb)
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->allocbuffer);
+
+ audinfo("Defer to lower allocbuffer\n");
+ return lower->ops->allocbuffer(lower, apb);
+}
+
+/****************************************************************************
+ * Name: pcm_freebuffer
+ *
+ * Description:
+ * Free an audio pipeline buffer. If the lower-level driver provides an
+ * allocbuffer routine, it should also provide the freebuffer routine to
+ * perform the free operation.
+ *
+ ****************************************************************************/
+
+static int pcm_freebuffer(FAR struct audio_lowerhalf_s *dev, FAR struct audio_buf_desc_s *apb)
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->freebuffer);
+
+ audinfo("Defer to lower freebuffer, apb=%p\n", apb);
+ return lower->ops->freebuffer(lower, apb);
+}
+
+/****************************************************************************
+ * Name: pcm_enqueuebuffer
+ *
+ * Description:
+ * Enqueue a buffer for processing. This is a non-blocking enqueue
+ * operation. If the lower-half driver's buffer queue is full, then it
+ * should return an error code of -ENOMEM, and the upper-half driver can
+ * decide to either block the calling thread or deal with it in a non-
+ * blocking manner.
+ *
+ * For each call to enqueuebuffer, the lower-half driver must call
+ * audio_dequeuebuffer when it is finished processing the bufferr, passing
+ * the previously enqueued apb and a dequeue status so that the upper-half
+ * driver can decide if a waiting thread needs to be release, if the
+ * dequeued buffer should be passed to the next block in the Audio
+ * Pipeline, etc.
+ *
+ ****************************************************************************/
+
+static int pcm_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb)
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+ apb_samp_t bytesleft;
+ int ret;
+
+ DEBUGASSERT(priv);
+ audinfo("Received buffer %p, streaming=%d\n", apb, priv->streaming);
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->enqueuebuffer && lower->ops->configure);
+
+ /* Are we streaming yet? */
+
+ if (priv->streaming) {
+ /* Yes, we are streaming */
+ /* Check for the last audio buffer in the stream */
+
+ if ((apb->flags & AUDIO_APB_FINAL) != 0) {
+ /* Yes.. then we are no longer streaming */
+
+ priv->streaming = false;
+ }
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+ audinfo("Received: apb=%p curbyte=%d nbytes=%d flags=%04x\n", apb, apb->curbyte, apb->nbytes, apb->flags);
+
+ /* Perform any necessary sub-sampling operations */
+
+ pcm_subsample(priv, apb);
+#endif
+
+ /* Then give the audio buffer to the lower driver */
+
+ audinfo("Pass to lower enqueuebuffer: apb=%p curbyte=%d nbytes=%d\n", apb, apb->curbyte, apb->nbytes);
+
+ return lower->ops->enqueuebuffer(lower, apb);
+ }
+
+ /* No.. then this must be the first buffer that we have seen (since we
+ * will error out out if the first buffer is smaller than the WAV file
+ * header. There is no attempt to reconstruct the full header from
+ * fragments in multiple, tiny audio buffers).
+ */
+
+ bytesleft = apb->nbytes - apb->curbyte;
+ audinfo("curbyte=%d nbytes=%d nmaxbytes=%d bytesleft=%d\n", apb->curbyte, apb->nbytes, apb->nmaxbytes, bytesleft);
+
+ if (bytesleft >= sizeof(struct wav_header_s)) {
+ /* Parse and verify the candidate PCM WAV file header */
+
+ if (pcm_parsewav(priv, &apb->samp[apb->curbyte])) {
+ struct audio_caps_s caps;
+
+ /* Configure the lower level for the number of channels, bitrate,
+ * and sample bitwidth.
+ */
+
+ DEBUGASSERT(priv->samprate < 65535);
+
+ caps.ac_len = sizeof(struct audio_caps_s);
+ caps.ac_type = AUDIO_TYPE_OUTPUT;
+ caps.ac_channels = priv->nchannels;
+
+ caps.ac_controls.hw[0] = (uint16_t) priv->samprate;
+ caps.ac_controls.b[2] = priv->bpsamp;
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->configure(lower, priv->session, &caps);
+#else
+ ret = lower->ops->configure(lower, &caps);
+#endif
+ if (ret < 0) {
+ auddbg("ERROR: Failed to set PCM configuration: %d\n", ret);
+ return ret;
+ }
+
+ /* Bump up the data offset */
+
+ apb->curbyte += sizeof(struct wav_header_s);
+
+#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
+ audinfo("Begin streaming: apb=%p curbyte=%d nbytes=%d\n", apb, apb->curbyte, apb->nbytes);
+
+ /* Perform any necessary sub-sampling operations */
+
+ pcm_subsample(priv, apb);
+#endif
+
+ /* Then give the audio buffer to the lower driver */
+
+ audinfo("Pass to lower enqueuebuffer: apb=%p curbyte=%d nbytes=%d\n", apb, apb->curbyte, apb->nbytes);
+
+ ret = lower->ops->enqueuebuffer(lower, apb);
+ if (ret == OK) {
+ /* Now we are streaming. Unless for some reason there is only
+ * one audio buffer in the audio stream. In that case, this
+ * will be marked as the final buffer
+ */
+
+ priv->streaming = ((apb->flags & AUDIO_APB_FINAL) == 0);
+ return OK;
+ }
+ }
+
+ auddbg("ERROR: Invalid PCM WAV file\n");
+
+ /* The normal protocol for streaming errors is as follows:
+ *
+ * (1) Fail the enqueueing by returned a negated error value. The
+ * upper level then knows that this buffer was not queue.
+ * (2) Return all queued buffers to the caller using the
+ * AUDIO_CALLBACK_DEQUEUE callback
+ * (3) Terminate playing using the AUDIO_CALLBACK_COMPLETE
+ * callback.
+ *
+ * In this case we fail on the very first buffer and we need only
+ * do (1) and (3).
+ */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ priv->export.upper(priv->export.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL);
+#else
+ priv->export.upper(priv->export.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
+#endif
+ }
+
+ /* This is not a WAV file! */
+
+ auddbg("ERROR: Invalid PCM WAV file\n");
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: pcm_cancelbuffer
+ *
+ * Description:
+ * Cancel a previously enqueued buffer.
+ *
+ ****************************************************************************/
+
+static int pcm_cancelbuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb)
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->cancelbuffer);
+
+ audinfo("Defer to lower cancelbuffer, apb=%p\n", apb);
+ return lower->ops->cancelbuffer(lower, apb);
+}
+
+/****************************************************************************
+ * Name: pcm_ioctl
+ *
+ * Description:
+ * Lower-half logic may support platform-specific ioctl commands.
+ *
+ ****************************************************************************/
+
+static int pcm_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, unsigned long arg)
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* Defer the operation to the lower device driver */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->ioctl);
+
+ audinfo("Defer to lower ioctl, cmd=%d arg=%ld\n");
+ return lower->ops->ioctl(lower, cmd, arg);
+}
+
+/****************************************************************************
+ * Name: pcm_reserve
+ *
+ * Description:
+ * Reserve a session (may only be one per device or may be multiple) for
+ * use by a client. Client software can open audio devices and issue
+ * AUDIOIOC_GETCAPS calls freely, but other operations require a
+ * reservation. A session reservation will assign a context that must
+ * be passed with
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_reserve(FAR struct audio_lowerhalf_s *dev, FAR void **session)
+#else
+static int pcm_reserve(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+ int ret;
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ DEBUGASSERT(priv && session);
+#else
+ DEBUGASSERT(priv);
+#endif
+
+ /* It is not necessary to reserve the upper half. What we really need to
+ * do is to reserved the lower device driver for exclusive use by the PCM
+ * decoder. That effectively reserves the upper PCM decoder along with
+ * the lower driver (which is then not available for use by other
+ * decoders).
+ *
+ * We do, however, need to remember the session returned by the lower
+ * level.
+ */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->reserve);
+
+ audinfo("Defer to lower reserve\n");
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ ret = lower->ops->reserve(lower, &priv->session);
+
+ /* Return a copy of the session to the caller */
+
+ *session = priv->session;
+
+#else
+ ret = lower->ops->reserve(lower);
+#endif
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: pcm_release
+ *
+ * Description:
+ * Release a session.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int pcm_release(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int pcm_release(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
+ FAR struct audio_lowerhalf_s *lower;
+
+ DEBUGASSERT(priv);
+
+ /* Release the lower driver.. it is then available for use by other
+ * decoders (and we cannot use the lower driver wither unless we re-
+ * reserve it).
+ */
+
+ lower = priv->lower;
+ DEBUGASSERT(lower && lower->ops->release);
+
+ audinfo("Defer to lower release\n");
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ return lower->ops->release(lower, session);
+#else
+ return lower->ops->release(lower);
+#endif
+}
+
+/****************************************************************************
+ * Name: pcm_callback
+ *
+ * Description:
+ * Lower-to-upper level callback for buffer dequeueing.
+ *
+ * Input Parameters:
+ * priv - The value of the 'priv' field from out audio_lowerhalf_s.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static void pcm_callback(FAR void *arg, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status, FAR void *session)
+#else
+static void pcm_callback(FAR void *arg, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status)
+#endif
+{
+ FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)arg;
+
+ DEBUGASSERT(priv && priv->export.upper);
+
+ /* The buffer belongs to to an upper level. Just forward the event to
+ * the next level up.
+ */
+
+#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: pcm_decode_initialize
+ *
+ * Description:
+ * Initialize the PCM device. The PCM device accepts and contains a
+ * low-level audio DAC-type device. It then returns a new audio lower
+ * half interface at adds a PCM decoding from end to the low-level
+ * audio device
+ *
+ * Input Parameters:
+ * dev - A reference to the low-level audio DAC-type device to contain.
+ *
+ * Returned Value:
+ * On success, a new audio device instance is returned that wraps the
+ * low-level device and provides a PCM decoding front end. NULL is
+ * returned on failure.
+ *
+ ****************************************************************************/
+
+FAR struct audio_lowerhalf_s *pcm_decode_initialize(FAR struct audio_lowerhalf_s *dev)
+{
+ FAR struct pcm_decode_s *priv;
+ FAR struct audio_ops_s *ops;
+
+ /* Allocate an instance of our private data structure */
+
+ priv = (FAR struct pcm_decode_s *)kmm_zalloc(sizeof(struct pcm_decode_s));
+ if (!priv) {
+ auddbg("ERROR: Failed to allocate driver structure\n");
+ return NULL;
+ }
+
+ /* Initialize our private data structure. Since kmm_zalloc() was used for
+ * the allocation, we need to initialize only non-zero, non-NULL, non-
+ * false fields.
+ */
+
+ /* Setup our operations */
+
+ ops = &priv->ops;
+ ops->getcaps = pcm_getcaps;
+ ops->configure = pcm_configure;
+ ops->shutdown = pcm_shutdown;
+ ops->start = pcm_start;
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+ ops->stop = pcm_stop;
+#endif
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+ ops->pause = pcm_pause;
+ ops->resume = pcm_resume;
+#endif
+
+ if (dev->ops->allocbuffer) {
+ DEBUGASSERT(dev->ops->freebuffer);
+ ops->allocbuffer = pcm_allocbuffer;
+ ops->freebuffer = pcm_freebuffer;
+ }
+
+ ops->enqueuebuffer = pcm_enqueuebuffer;
+ ops->cancelbuffer = pcm_cancelbuffer;
+ ops->ioctl = pcm_ioctl;
+ ops->reserve = pcm_reserve;
+ ops->release = pcm_release;
+
+ /* Set up our struct audio_lower_half that we will register with the
+ * system. The registration process will fill in the priv->export.upper
+ * and priv fields with the correct callback information.
+ */
+
+ priv->export.ops = &priv->ops;
+
+ /* Save the struct audio_lower_half of the low-level audio device. Set
+ * out callback information for the lower-level audio device. Our
+ * callback will simply forward to the upper callback.
+ */
+
+ priv->lower = dev;
+ dev->upper = pcm_callback;
+ dev->priv = priv;
+
+ return &priv->export;
+}
+
+#endif /* CONFIG_AUDIO && CONFIG_AUDIO_FORMAT_PCM */
if I2S
endif # I2S
+menuconfig AUDIO_DEVICES
+ bool "Audio Device Support"
+ default n
+ ---help---
+ Enable support for audio device drivers. This includes drivers for
+ MP3, WMA and Ogg Vorbis encoding, decoding, as well as drivers for
+ interfacing with external DSP chips to perform custom audio functions.
+
+ NOTE: All of these drivers depend on support from the audio subsystem
+ enabled with the AUDIO selection.
+
+if AUDIO_DEVICES
+source drivers/audio/Kconfig
+endif # AUDIO_DEVICES
+
menuconfig BCH
bool "Block-to-Character (BCH) Support"
default n
include analog$(DELIM)Make.defs
include bch$(DELIM)Make.defs
include i2c$(DELIM)Make.defs
+include audio$(DELIM)Make.defs
include lcd$(DELIM)Make.defs
include net$(DELIM)Make.defs
include pipes$(DELIM)Make.defs
--- /dev/null
+#
+# For a description of the syntax of this configuration file,
+# see kconfig-language at https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt
+#
+
+config AUDIO_I2SCHAR
+ bool "I2S character driver (for testing only)"
+ default n
+ depends on I2S && AUDIO
+ ---help---
+ This selection enables a simple character driver that supports I2S
+ transfers via a read() and write(). The intent of this driver is to
+ support I2S testing. It is not an audio driver but does conform to
+ some of the buffer management heuristics of an audio driver. It is
+ not suitable for use in any real driver application in its current
+ form.
+
+if AUDIO_I2SCHAR
+
+config AUDIO_I2SCHAR_RXTIMEOUT
+ int "RX timeout"
+ default 0
+ ---help---
+ This is a fixed timeout value that will be used for all receiver
+ transfers. This is in units of system clock ticks (configurable).
+ The special value of zero disables RX timeouts. Default: 0
+
+config AUDIO_I2SCHAR_TXTIMEOUT
+ int "TX timeout"
+ default 0
+ ---help---
+ This is a fixed timeout value that will be used for all transmitter
+ transfers. This is in units of system clock ticks (configurable).
+ The special value of zero disables RX timeouts. Default: 0
+
+endif #AUDIO_I2SCHAR
+
+
+config AUDIO_NULL
+ bool "NULL audio device"
+ default n
+ depends on AUDIO
+ ---help---
+ A do-nothinig audio device driver to simplify testing of audio
+ decoders.
+
+if AUDIO_NULL
+
+config AUDIO_NULL_MSG_PRIO
+ int "Null audio device message priority"
+ default 1
+
+config AUDIO_NULL_BUFFER_SIZE
+ int "Null audio device preferred buffer size"
+ default 8192
+
+config AUDIO_NULL_NUM_BUFFERS
+ int "Null audio device preferred number of buffers"
+ default 4
+
+config AUDIO_NULL_WORKER_STACKSIZE
+ int "Null audio device worker thread stack size"
+ default 768
+
+endif # AUDIO_NULL
+
-############################################################################
+###########################################################################
#
# Copyright 2017 Samsung Electronics All Rights Reserved.
#
# either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
#
-############################################################################
+###########################################################################
############################################################################
# drivers/audio/Make.defs
# These drivers support various Audio devices using the NuttX Audio
ifeq ($(CONFIG_AUDIO_DEVICES),y)
+ifeq ($(CONFIG_AUDIO_NULL),y)
+CSRCS += audio_null.c
+endif
+
ifeq ($(CONFIG_AUDIO_I2SCHAR),y)
CSRCS += i2schar.c
endif
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * drivers/audio/audio_null.c
+ *
+ * A do-nothinig audio device driver to simplify testing of audio decoders.
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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 <tinyara/config.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <queue.h>
+#include <debug.h>
+
+#include <tinyara/kmalloc.h>
+#include <tinyara/fs/fs.h>
+#include <tinyara/fs/ioctl.h>
+#include <tinyara/audio/audio.h>
+#include <tinyara/audio/audio_null.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct null_dev_s {
+ struct audio_lowerhalf_s dev; /* Audio lower half (this device) */
+ mqd_t mq; /* Message queue for receiving messages */
+ char mqname[16]; /* Our message queue name */
+ pthread_t threadid; /* ID of our thread */
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+ volatile bool terminate; /* True: request to terminate */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int null_getcaps(FAR struct audio_lowerhalf_s *dev, int type, FAR struct audio_caps_s *caps);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_configure(FAR struct audio_lowerhalf_s *dev, FAR void *session, FAR const struct audio_caps_s *caps);
+#else
+static int null_configure(FAR struct audio_lowerhalf_s *dev, FAR const struct audio_caps_s *caps);
+#endif
+static int null_shutdown(FAR struct audio_lowerhalf_s *dev);
+static void *null_workerthread(pthread_addr_t pvarg);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_start(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+static int null_start(FAR struct audio_lowerhalf_s *dev);
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+static int null_stop(FAR struct audio_lowerhalf_s *dev);
+#endif
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+static int null_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+static int null_pause(FAR struct audio_lowerhalf_s *dev);
+static int null_resume(FAR struct audio_lowerhalf_s *dev);
+#endif
+#endif
+static int null_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb);
+static int null_cancelbuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb);
+static int null_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, unsigned long arg);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_reserve(FAR struct audio_lowerhalf_s *dev, FAR void **session);
+#else
+static int null_reserve(FAR struct audio_lowerhalf_s *dev);
+#endif
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_release(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+static int null_release(FAR struct audio_lowerhalf_s *dev);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct audio_ops_s g_audioops = {
+ null_getcaps, /* getcaps */
+ null_configure, /* configure */
+ null_shutdown, /* shutdown */
+ null_start, /* start */
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+ null_stop, /* stop */
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+ null_pause, /* pause */
+ null_resume, /* resume */
+#endif
+ NULL, /* allocbuffer */
+ NULL, /* freebuffer */
+ null_enqueuebuffer, /* enqueue_buffer */
+ null_cancelbuffer, /* cancel_buffer */
+ null_ioctl, /* ioctl */
+ NULL, /* read */
+ NULL, /* write */
+ null_reserve, /* reserve */
+ null_release /* release */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: null_getcaps
+ *
+ * Description: Get the audio device capabilities
+ *
+ ****************************************************************************/
+
+static int null_getcaps(FAR struct audio_lowerhalf_s *dev, int type, FAR struct audio_caps_s *caps)
+{
+ audinfo("type=%d\n", type);
+
+ /* Validate the structure */
+
+ DEBUGASSERT(caps->ac_len >= sizeof(struct audio_caps_s));
+
+ /* Fill in the caller's structure based on requested info */
+
+ caps->ac_format.hw = 0;
+ caps->ac_controls.w = 0;
+
+ switch (caps->ac_type) {
+ /* Caller is querying for the types of units we support */
+
+ case AUDIO_TYPE_QUERY:
+
+ /* Provide our overall capabilities. The interfacing software
+ * must then call us back for specific info for each capability.
+ */
+
+ caps->ac_channels = 2; /* Stereo output */
+
+ switch (caps->ac_subtype) {
+ case AUDIO_TYPE_QUERY:
+ /* We don't decode any formats! Only something above us in
+ * the audio stream can perform decoding on our behalf.
+ */
+
+ /* The types of audio units we implement */
+
+ caps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE | AUDIO_TYPE_PROCESSING;
+
+ break;
+
+ case AUDIO_FMT_MIDI:
+ /* We only support Format 0 */
+
+ caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
+ break;
+
+ default:
+ caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
+ break;
+ }
+
+ break;
+
+ /* Provide capabilities of our OUTPUT unit */
+
+ case AUDIO_TYPE_OUTPUT:
+
+ caps->ac_channels = 2;
+
+ switch (caps->ac_subtype) {
+ case AUDIO_TYPE_QUERY:
+
+ /* Report the Sample rates we support */
+
+ caps->ac_controls.b[0] = AUDIO_SAMP_RATE_8K | AUDIO_SAMP_RATE_11K | AUDIO_SAMP_RATE_16K | AUDIO_SAMP_RATE_22K | AUDIO_SAMP_RATE_32K | AUDIO_SAMP_RATE_44K | AUDIO_SAMP_RATE_48K;
+ break;
+
+ case AUDIO_FMT_MP3:
+ case AUDIO_FMT_WMA:
+ case AUDIO_FMT_PCM:
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ /* Provide capabilities of our FEATURE units */
+
+ case AUDIO_TYPE_FEATURE:
+
+ /* If the sub-type is UNDEF, then report the Feature Units we support */
+
+ if (caps->ac_subtype == AUDIO_FU_UNDEF) {
+ /* Fill in the ac_controls section with the Feature Units we have */
+
+ caps->ac_controls.b[0] = AUDIO_FU_VOLUME | AUDIO_FU_BASS | AUDIO_FU_TREBLE;
+ caps->ac_controls.b[1] = AUDIO_FU_BALANCE >> 8;
+ } else {
+ /* TODO: Do we need to provide specific info for the Feature Units,
+ * such as volume setting ranges, etc.?
+ */
+ }
+
+ break;
+
+ /* Provide capabilities of our PROCESSING unit */
+
+ case AUDIO_TYPE_PROCESSING:
+
+ switch (caps->ac_subtype) {
+ case AUDIO_PU_UNDEF:
+
+ /* Provide the type of Processing Units we support */
+
+ caps->ac_controls.b[0] = AUDIO_PU_STEREO_EXTENDER;
+ break;
+
+ case AUDIO_PU_STEREO_EXTENDER:
+
+ /* Provide capabilities of our Stereo Extender */
+
+ caps->ac_controls.b[0] = AUDIO_STEXT_ENABLE | AUDIO_STEXT_WIDTH;
+ break;
+
+ default:
+
+ /* Other types of processing uint we don't support */
+
+ break;
+ }
+
+ break;
+
+ /* All others we don't support */
+
+ default:
+
+ /* Zero out the fields to indicate no support */
+
+ caps->ac_subtype = 0;
+ caps->ac_channels = 0;
+
+ break;
+ }
+
+ /* Return the length of the audio_caps_s struct for validation of
+ * proper Audio device type.
+ */
+
+ audinfo("Return %d\n", caps->ac_len);
+ return caps->ac_len;
+}
+
+/****************************************************************************
+ * Name: null_configure
+ *
+ * Description:
+ * Configure the audio device for the specified mode of operation.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_configure(FAR struct audio_lowerhalf_s *dev, FAR void *session, FAR const struct audio_caps_s *caps)
+#else
+static int null_configure(FAR struct audio_lowerhalf_s *dev, FAR const struct audio_caps_s *caps)
+#endif
+{
+ audinfo("ac_type: %d\n", caps->ac_type);
+
+ /* Process the configure operation */
+
+ switch (caps->ac_type) {
+ case AUDIO_TYPE_FEATURE:
+ audinfo(" AUDIO_TYPE_FEATURE\n");
+
+ /* Process based on Feature Unit */
+
+ switch (caps->ac_format.hw) {
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+ case AUDIO_FU_VOLUME:
+ audinfo(" Volume: %d\n", caps->ac_controls.hw[0]);
+ break;
+#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_TONE
+ case AUDIO_FU_BASS:
+ audinfo(" Bass: %d\n", caps->ac_controls.b[0]);
+ break;
+
+ case AUDIO_FU_TREBLE:
+ audinfo(" Treble: %d\n", caps->ac_controls.b[0]);
+ break;
+#endif /* CONFIG_AUDIO_EXCLUDE_TONE */
+
+ default:
+ auderr(" ERROR: Unrecognized feature unit\n");
+ break;
+ }
+ break;
+
+ case AUDIO_TYPE_OUTPUT:
+ audinfo(" AUDIO_TYPE_OUTPUT:\n");
+ audinfo(" Number of channels: %u\n", caps->ac_channels);
+ audinfo(" Sample rate: %u\n", caps->ac_controls.hw[0]);
+ audinfo(" Sample width: %u\n", caps->ac_controls.b[2]);
+ break;
+
+ case AUDIO_TYPE_PROCESSING:
+ audinfo(" AUDIO_TYPE_PROCESSING:\n");
+ break;
+ }
+
+ audinfo("Return OK\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: null_shutdown
+ *
+ * Description:
+ * Shutdown the driver and put it in the lowest power state possible.
+ *
+ ****************************************************************************/
+
+static int null_shutdown(FAR struct audio_lowerhalf_s *dev)
+{
+ audinfo("Return OK\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: null_workerthread
+ *
+ * This is the thread that feeds data to the chip and keeps the audio
+ * stream going.
+ *
+ ****************************************************************************/
+
+static void *null_workerthread(pthread_addr_t pvarg)
+{
+ FAR struct null_dev_s *priv = (struct null_dev_s *)pvarg;
+ struct audio_msg_s msg;
+ int msglen;
+ int prio;
+
+ audinfo("Entry\n");
+
+ /* Loop as long as we are supposed to be running */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+ while (!priv->terminate)
+#else
+ for (;;)
+#endif
+ {
+ /* Wait for messages from our message queue */
+
+ msglen = mq_receive(priv->mq, (FAR char *)&msg, sizeof(msg), &prio);
+
+ /* Handle the case when we return with no message */
+
+ if (msglen < sizeof(struct audio_msg_s)) {
+ auderr("ERROR: Message too small: %d\n", msglen);
+ continue;
+ }
+
+ /* Process the message */
+
+ switch (msg.msgId) {
+ case AUDIO_MSG_DATA_REQUEST:
+ break;
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+ case AUDIO_MSG_STOP:
+ priv->terminate = true;
+ break;
+#endif
+
+ case AUDIO_MSG_ENQUEUE:
+ break;
+
+ case AUDIO_MSG_COMPLETE:
+ break;
+
+ default:
+ auderr("ERROR: Ignoring message ID %d\n", msg.msgId);
+ break;
+ }
+ }
+
+ /* Close the message queue */
+
+ mq_close(priv->mq);
+ mq_unlink(priv->mqname);
+ priv->mq = NULL;
+
+ /* Send an AUDIO_MSG_COMPLETE message to the client */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL);
+#else
+ priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
+#endif
+
+ audinfo("Exit\n");
+ return NULL;
+}
+
+/****************************************************************************
+ * Name: null_start
+ *
+ * Description:
+ * Start the configured operation (audio streaming, volume enabled, etc.).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_start(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int null_start(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ FAR struct null_dev_s *priv = (FAR struct null_dev_s *)dev;
+ struct sched_param sparam;
+ struct mq_attr attr;
+ pthread_attr_t tattr;
+ FAR void *value;
+ int ret;
+
+ audinfo("Entry\n");
+
+ /* Create a message queue for the worker thread */
+
+ snprintf(priv->mqname, sizeof(priv->mqname), "/tmp/%X", priv);
+
+ attr.mq_maxmsg = 16;
+ attr.mq_msgsize = sizeof(struct audio_msg_s);
+ attr.mq_curmsgs = 0;
+ attr.mq_flags = 0;
+
+ priv->mq = mq_open(priv->mqname, O_RDWR | O_CREAT, 0644, &attr);
+ if (priv->mq == NULL) {
+ /* Error creating message queue! */
+
+ auderr("ERROR: Couldn't allocate message queue\n");
+ return -ENOMEM;
+ }
+
+ /* Join any old worker thread we had created to prevent a memory leak */
+
+ if (priv->threadid != 0) {
+ audinfo("Joining old thread\n");
+ pthread_join(priv->threadid, &value);
+ }
+
+ /* Start our thread for sending data to the device */
+
+ pthread_attr_init(&tattr);
+ sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 3;
+ (void)pthread_attr_setschedparam(&tattr, &sparam);
+ (void)pthread_attr_setstacksize(&tattr, CONFIG_AUDIO_NULL_WORKER_STACKSIZE);
+
+ audinfo("Starting worker thread\n");
+ ret = pthread_create(&priv->threadid, &tattr, null_workerthread, (pthread_addr_t) priv);
+ if (ret != OK) {
+ auderr("ERROR: pthread_create failed: %d\n", ret);
+ } else {
+ pthread_setname_np(priv->threadid, "null audio");
+ audinfo("Created worker thread\n");
+ }
+
+ audinfo("Return %d\n", ret);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: null_stop
+ *
+ * Description: Stop the configured operation (audio streaming, volume
+ * disabled, etc.).
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int null_stop(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ FAR struct null_dev_s *priv = (FAR struct null_dev_s *)dev;
+ struct audio_msg_s term_msg;
+ FAR void *value;
+
+ /* Send a message to stop all audio streaming */
+ /* REVISIT: There should be a check to see if the worker thread is still
+ * running.
+ */
+
+ term_msg.msgId = AUDIO_MSG_STOP;
+ term_msg.u.data = 0;
+ mq_send(priv->mq, (FAR const char *)&term_msg, sizeof(term_msg), CONFIG_AUDIO_NULL_MSG_PRIO);
+
+ /* Join the worker thread */
+
+ pthread_join(priv->threadid, &value);
+ priv->threadid = 0;
+
+ audinfo("Return OK\n");
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: null_pause
+ *
+ * Description: Pauses the playback.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int null_pause(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ audinfo("Return OK\n");
+ return OK;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
+
+/****************************************************************************
+ * Name: null_resume
+ *
+ * Description: Resumes the playback.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int null_resume(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ audinfo("Return OK\n");
+ return OK;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
+
+/****************************************************************************
+ * Name: null_enqueuebuffer
+ *
+ * Description: Enqueue an Audio Pipeline Buffer for playback/ processing.
+ *
+ ****************************************************************************/
+
+static int null_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb)
+{
+ FAR struct null_dev_s *priv = (FAR struct null_dev_s *)dev;
+ bool done;
+
+ audinfo("apb=%p curbyte=%d nbytes=%d\n", apb, apb->curbyte, apb->nbytes);
+
+ /* Say that we consumed all of the data */
+
+ apb->curbyte = apb->nbytes;
+
+ /* Check if this was the last buffer in the stream */
+
+ done = ((apb->flags & AUDIO_APB_FINAL) != 0);
+
+ /* And return the buffer to the upper level */
+
+ DEBUGASSERT(priv && apb && priv->dev.upper);
+
+ /* The buffer belongs to to an upper level. Just forward the event to
+ * the next level up.
+ */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL);
+#else
+ priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
+#endif
+
+ /* Say we are done playing if this was the last buffer in the stream */
+
+ if (done) {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL);
+#else
+ priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
+#endif
+ }
+
+ audinfo("Return OK\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: null_cancelbuffer
+ *
+ * Description: Called when an enqueued buffer is being cancelled.
+ *
+ ****************************************************************************/
+
+static int null_cancelbuffer(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb)
+{
+ audinfo("apb=%p curbyte=%d nbytes=%d, return OK\n", apb, apb->curbyte, apb->nbytes);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: null_ioctl
+ *
+ * Description: Perform a device ioctl
+ *
+ ****************************************************************************/
+
+static int null_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+ FAR struct ap_buffer_info_s *bufinfo;
+#endif
+
+ audinfo("cmd=%d arg=%ld\n");
+
+ /* Deal with ioctls passed from the upper-half driver */
+
+ switch (cmd) {
+ /* Check for AUDIOIOC_HWRESET ioctl. This ioctl is passed straight
+ * through from the upper-half audio driver.
+ */
+ case AUDIOIOC_HWRESET: {
+ audinfo("AUDIOIOC_HWRESET:\n");
+ }
+ break;
+ /* Report our preferred buffer size and quantity */
+
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+ case AUDIOIOC_GETBUFFERINFO: {
+ audinfo("AUDIOIOC_GETBUFFERINFO:\n");
+ bufinfo = (FAR struct ap_buffer_info_s *)arg;
+ bufinfo->buffer_size = CONFIG_AUDIO_NULL_BUFFER_SIZE;
+ bufinfo->nbuffers = CONFIG_AUDIO_NULL_NUM_BUFFERS;
+ }
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ audinfo("Return OK\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: null_reserve
+ *
+ * Description: Reserves a session (the only one we have).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_reserve(FAR struct audio_lowerhalf_s *dev, FAR void **session)
+#else
+static int null_reserve(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ audinfo("Return OK\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: null_release
+ *
+ * Description: Releases the session (the only one we have).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int null_release(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int null_release(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+ FAR struct null_dev_s *priv = (FAR struct null_dev_s *)dev;
+ void *value;
+
+ /* Join any old worker thread we had created to prevent a memory leak */
+
+ if (priv->threadid != 0) {
+ pthread_join(priv->threadid, &value);
+ priv->threadid = 0;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: audio_null_initialize
+ *
+ * Description:
+ * Initialize the null audio device.
+ *
+ * Input Parameters:
+ * i2c - An I2C driver instance
+ * i2s - An I2S driver instance
+ * lower - Persistent board configuration data
+ *
+ * Returned Value:
+ * A new lower half audio interface for the NULL audio device is returned
+ * on success; NULL is returned on failure.
+ *
+ ****************************************************************************/
+
+FAR struct audio_lowerhalf_s *audio_null_initialize(void)
+{
+ FAR struct null_dev_s *priv;
+
+ /* Allocate the null audio device structure */
+
+ priv = (FAR struct null_dev_s *)kmm_zalloc(sizeof(struct null_dev_s));
+ if (priv) {
+ /* Initialize the null audio device structure. Since we used kmm_zalloc,
+ * only the non-zero elements of the structure need to be initialized.
+ */
+
+ priv->dev.ops = &g_audioops;
+ return &priv->dev;
+ }
+
+ auderr("ERROR: Failed to allocate null audio device\n");
+ return NULL;
+}
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * drivers/audio/i2schar.c
+ *
+ * This is a simple character driver for testing I2C. It is not an audio
+ * driver but does conform to some of the buffer management heuristics of an
+ * audio driver. It is not suitable for use in any real driver application
+ * in its current form.
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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 <tinyara/config.h>
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <tinyara/kmalloc.h>
+#include <tinyara/fs/fs.h>
+#include <tinyara/audio/audio.h>
+#include <tinyara/audio/i2s.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_AUDIO_I2SCHAR_RXTIMEOUT
+#define CONFIG_AUDIO_I2SCHAR_RXTIMEOUT 0
+#endif
+
+#ifndef CONFIG_AUDIO_I2SCHAR_TXTIMEOUT
+#define CONFIG_AUDIO_I2SCHAR_TXTIMEOUT 0
+#endif
+
+/* Device naming ************************************************************/
+#define DEVNAME_FMT "/dev/i2schar%d"
+#define DEVNAME_FMTLEN (12 + 3 + 1)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct i2schar_dev_s {
+ FAR struct i2s_dev_s *i2s; /* The lower half i2s driver */
+ sem_t exclsem; /* Assures mutually exclusive access */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* I2S callback function */
+
+static void i2schar_rxcallback(FAR struct i2s_dev_s *dev, FAR struct ap_buffer_s *apb, FAR void *arg, int result);
+static void i2schar_txcallback(FAR struct i2s_dev_s *dev, FAR struct ap_buffer_s *apb, FAR void *arg, int result);
+
+/* Character driver methods */
+
+static ssize_t i2schar_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
+static ssize_t i2schar_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations i2schar_fops = {
+ NULL, /* open */
+ NULL, /* close */
+ i2schar_read, /* read */
+ i2schar_write, /* write */
+ NULL, /* seek */
+ NULL, /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ NULL, /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i2schar_rxcallback
+ *
+ * Description:
+ * I2S RX transfer complete callback.
+ *
+ * NOTE: In this test driver, the RX is simply dumped in the bit bucket.
+ * You would not do this in a real application. You would return the
+ * received data to the caller via some IPC.
+ *
+ * Also, the test buffer is simply freed. This will work if this driver
+ * has the sole reference to buffer; in that case the buffer will be freed.
+ * Otherwise -- memory leak! A more efficient design would recyle the
+ * audio buffers.
+ *
+ ****************************************************************************/
+
+static void i2schar_rxcallback(FAR struct i2s_dev_s *dev, FAR struct ap_buffer_s *apb, FAR void *arg, int result)
+{
+ FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
+
+ DEBUGASSERT(priv && apb);
+ llvdbg("apb=%p nbytes=%d result=%d\n", apb, apb->nbytes, result);
+
+ /* REVISIT: If you want this to actually do something other than
+ * test I2S data transfer, then this is the point where you would
+ * want to pass the received I2S to some application.
+ */
+
+ /* Release our reference to the audio buffer. Hopefully it will be freed
+ * now.
+ */
+
+ llvdbg("Freeing apb=%p crefs=%d\n", apb, apb->crefs);
+ apb_free(apb);
+}
+
+/****************************************************************************
+ * Name: i2schar_txcallback
+ *
+ * Description:
+ * I2S TX transfer complete callback
+ *
+ * NOTE: The test buffer is simply freed. This will work if this driver
+ * has the sole reference to buffer; in that case the buffer will be freed.
+ * Otherwise -- memory leak! A more efficient design would recyle the
+ * audio buffers.
+ *
+ ****************************************************************************/
+
+static void i2schar_txcallback(FAR struct i2s_dev_s *dev, FAR struct ap_buffer_s *apb, FAR void *arg, int result)
+{
+ FAR struct i2schar_dev_s *priv = (FAR struct i2schar_dev_s *)arg;
+
+ DEBUGASSERT(priv && apb);
+ llvdbg("apb=%p nbytes=%d result=%d\n", apb, apb->nbytes, result);
+
+ /* REVISIT: If you want this to actually do something other than
+ * test I2S data transfer, then this is the point where you would
+ * want to let some application know that the transfer has complete.
+ */
+
+ /* Release our reference to the audio buffer. Hopefully it will be freed
+ * now.
+ */
+
+ llvdbg("Freeing apb=%p crefs=%d\n", apb, apb->crefs);
+ apb_free(apb);
+}
+
+/****************************************************************************
+ * Name: i2schar_read
+ *
+ * Description:
+ * Standard character driver read method
+ *
+ ****************************************************************************/
+
+static ssize_t i2schar_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct i2schar_dev_s *priv;
+ FAR struct ap_buffer_s *apb;
+ size_t nbytes;
+ int ret;
+
+ llvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);
+
+ /* Get our private data structure */
+
+ DEBUGASSERT(filep && buffer);
+
+ inode = filep->f_inode;
+ DEBUGASSERT(inode);
+
+ priv = (FAR struct i2schar_dev_s *)inode->i_private;
+ DEBUGASSERT(priv);
+
+ /* Verify that the buffer refers to one, correctly sized audio buffer */
+
+ DEBUGASSERT(buflen >= sizeof(struct ap_buffer_s));
+
+ apb = (FAR struct ap_buffer_s *)buffer;
+ nbytes = apb->nmaxbytes;
+ DEBUGASSERT(buflen >= (sizeof(struct ap_buffer_s) + nbytes));
+
+ /* Add a reference to the audio buffer */
+
+ apb_reference(apb);
+
+ /* Get exclusive access to i2c character driver */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0) {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+ i2serr("ERROR: sem_wait returned: %d\n", ret);
+ goto errout_with_reference;
+ }
+
+ /* Give the buffer to the I2S driver */
+
+ ret = I2S_RECEIVE(priv->i2s, apb, i2schar_rxcallback, priv, CONFIG_AUDIO_I2SCHAR_RXTIMEOUT);
+ if (ret < 0) {
+ i2serr("ERROR: I2S_RECEIVE returned: %d\n", ret);
+ goto errout_with_reference;
+ }
+
+ /* Lie to the caller and tell them that all of the bytes have been
+ * received
+ */
+
+ sem_post(&priv->exclsem);
+ return sizeof(struct ap_buffer_s) + nbytes;
+
+errout_with_reference:
+ apb_free(apb);
+ sem_post(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i2schar_write
+ *
+ * Description:
+ * Standard character driver write method
+ *
+ ****************************************************************************/
+
+static ssize_t i2schar_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct i2schar_dev_s *priv;
+ FAR struct ap_buffer_s *apb;
+ size_t nbytes;
+ int ret;
+
+ llvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);
+
+ /* Get our private data structure */
+
+ DEBUGASSERT(filep && buffer);
+
+ inode = filep->f_inode;
+ DEBUGASSERT(inode);
+
+ priv = (FAR struct i2schar_dev_s *)inode->i_private;
+ DEBUGASSERT(priv);
+
+ /* Verify that the buffer refers to one, correctly sized audio buffer */
+
+ DEBUGASSERT(buflen >= sizeof(struct ap_buffer_s));
+
+ apb = (FAR struct ap_buffer_s *)buffer;
+ nbytes = apb->nmaxbytes;
+ DEBUGASSERT(buflen >= (sizeof(struct ap_buffer_s) + nbytes));
+
+ /* Add a reference to the audio buffer */
+
+ apb_reference(apb);
+
+ /* Get exclusive access to i2c character driver */
+
+ ret = sem_wait(&priv->exclsem);
+ if (ret < 0) {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+ i2serr("ERROR: sem_wait returned: %d\n", ret);
+ goto errout_with_reference;
+ }
+
+ /* Give the audio buffer to the I2S driver */
+
+ ret = I2S_SEND(priv->i2s, apb, i2schar_txcallback, priv, CONFIG_AUDIO_I2SCHAR_TXTIMEOUT);
+ if (ret < 0) {
+ i2serr("ERROR: I2S_SEND returned: %d\n", ret);
+ goto errout_with_reference;
+ }
+
+ /* Lie to the caller and tell them that all of the bytes have been
+ * sent.
+ */
+
+ sem_post(&priv->exclsem);
+ return sizeof(struct ap_buffer_s) + nbytes;
+
+errout_with_reference:
+ apb_free(apb);
+ sem_post(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i2schar_register
+ *
+ * Description:
+ * Create and register the I2S character driver.
+ *
+ * The I2S character driver is a simple character driver that supports I2S
+ * transfers via a read() and write(). The intent of this driver is to
+ * support I2S testing. It is not an audio driver but does conform to some
+ * of the buffer management heuristics of an audio driver. It is not
+ * suitable for use in any real driver application in its current form.
+ *
+ * Input Parameters:
+ * i2s - An instance of the lower half I2S driver
+ * minor - The device minor number. The I2S character device will be
+ * registers as /dev/i2scharN where N is the minor number
+ *
+ * Returned Value:
+ * OK if the driver was successfully register; A negated errno value is
+ * returned on any failure.
+ *
+ ****************************************************************************/
+
+int i2schar_register(FAR struct i2s_dev_s *i2s, int minor)
+{
+ FAR struct i2schar_dev_s *priv;
+ char devname[DEVNAME_FMTLEN];
+ int ret;
+
+ /* Sanity check */
+
+ DEBUGASSERT(i2s != NULL && (unsigned)minor < 1000);
+
+ /* Allocate a I2S character device structure */
+
+ priv = (FAR struct i2schar_dev_s *)kmm_zalloc(sizeof(struct i2schar_dev_s));
+ if (priv) {
+ /* Initialize the I2S character device structure */
+
+ priv->i2s = i2s;
+ sem_init(&priv->exclsem, 0, 1);
+
+ /* Create the character device name */
+
+ snprintf(devname, DEVNAME_FMTLEN, DEVNAME_FMT, minor);
+ ret = register_driver(devname, &i2schar_fops, 0666, priv);
+ if (ret < 0) {
+ /* Free the device structure if we failed to create the character
+ * device.
+ */
+
+ kmm_free(priv);
+ }
+
+ /* Return the result of the registration */
+
+ return OK;
+ }
+
+ return -ENOMEM;
+}
#define lllvdbg(x...)
#endif
+#ifdef CONFIG_DEBUG_AUDIO_ERROR
+#define auddbg(format, ...) dbg(format, ##__VA_ARGS__)
+#define audlldbg(format, ...) lldbg(format, ##__VA_ARGS__)
+#else
+#define auddbg(x...)
+#define audlldbg(x...)
+#endif
+
+#ifdef CONFIG_DEBUG_AUDIO_WARN
+#define audwdbg(format, ...) wdbg(format, ##__VA_ARGS__)
+#define audllwdbg(format, ...) llwdbg(format, ##__VA_ARGS__)
+#else
+#define audwdbg(x...)
+#define audllwdbg(x...)
+#endif
+
+#ifdef CONFIG_DEBUG_AUDIO_INFO
+#define audvdbg(format, ...) vdbg(format, ##__VA_ARGS__)
+#define audllvdbg(format, ...) llvdbg(format, ##__VA_ARGS__)
+#else
+#define audvdbg(x...)
+#define audllvdbg(x...)
+#endif
+
#ifdef CONFIG_NET_LWIP_DEBUG
#define lwipdbg(format, ...) dbg(format, ##__VA_ARGS__)
#define lwiplldbg(format, ...) lldbg(format, ##__VA_ARGS__)
#define ttdbg(format, ...)
#endif
+
#else /* CONFIG_CPP_HAVE_VARARGS */
/* Variadic macros NOT supported */
#define lllvdbg (void)
#endif
+#ifdef CONFIG_DEBUG_AUDIO_ERROR
+#define auddbg dbg
+#define audlldbg lldbg
+#else
+#define auddbg (void)
+#define audlldbg (void)
+#endif
+
+#ifdef CONFIG_DEBUG_AUDIO_WARN
+#define audwdbg wdbg
+#define audllwdbg llwdbg
+#else
+#define audwdbg (void)
+#define audllwdbg (void)
+#endif
+
+#ifdef CONFIG_DEBUG_AUDIO_INFO
+#define audvdbg vdbg
+#define audllvdbg llvdbg
+#else
+#define audvdbg (void)
+#define audllvdbg (void)
+#endif
+
#endif /* CONFIG_CPP_HAVE_VARARGS */
/* Buffer dumping macros do not depend on varargs */
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * include/tinyara/audio/audio.h
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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_TINYARA_AUDIO_AUDIO_H
+#define __INCLUDE_TINYARA_AUDIO_AUDIO_H
+
+/* For the purposes of this driver, an Audio device is any device that
+ * generates, records, mixes, or otherwise modifies audio data in any format,
+ * such as PCM, MP3, AAC, etc.
+ *
+ * The Audio driver is split into two parts:
+ *
+ * 1) An "upper half", generic driver that provides the comman Audio interface
+ * to application level code, and
+ * 2) A "lower half", platform-specific driver that implements the low-level
+ * controls to configure and communicate with the audio device(s).
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <tinyara/config.h>
+#include <tinyara/compiler.h>
+
+#include <tinyara/fs/ioctl.h>
+#include <tinyara/spi/spi.h>
+#include <queue.h>
+#include <semaphore.h>
+
+#ifdef CONFIG_AUDIO
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* CONFIG_AUDIO - Enables Audio driver support
+ * CONFIG_DEBUG_AUDIO - If enabled (with CONFIG_DEBUG_FEATURES and, optionally,
+ * CONFIG_DEBUG_INFO), this will generate output that can be used to
+ * debug Audio drivers.
+ */
+
+/* IOCTL Commands ***********************************************************/
+/* The Audio module uses a standard character driver framework. However, a
+ * lot of the Audio driver functionality is configured via a device control
+ * interface, such as sampling rate, volume, data format, etc.
+ * The Audio ioctl commands are listed below:
+ *
+ * AUDIOIOC_GETCAPS - Get the Audio Device Capabilities
+ *
+ * ioctl argument: Pointer to the audio_caps_s structure to receive the
+ * capabilities info. The "len" and "type" fields should
+ * be filled in prior to calling this ioctl. To get
+ * overall capabilities, specify the type as
+ * AUDIO_TYPE_QUERY, otherwise specify any type that was
+ * reported by the device during the QUERY.
+ *
+ * AUDIOIOC_CONFIGURE - Configure device for the specified mode
+ *
+ * ioctl argument: Pointer to the audio_caps_s structure which identifies
+ * the capabilities to configure for.
+ *
+ * AUDIOIOC_SHUTDOWN - Shutdown the device.
+ *
+ * ioctl argument: None
+ *
+ * AUDIOIOC_START - Start Audio streaming
+ *
+ * ioctl argument: None
+ *
+ * AUDIOIOC_STOP - Stop Audio streaming
+ *
+ * ioctl argument: None
+ */
+
+#define AUDIOIOC_GETCAPS _AUDIOIOC(1)
+#define AUDIOIOC_RESERVE _AUDIOIOC(2)
+#define AUDIOIOC_RELEASE _AUDIOIOC(3)
+#define AUDIOIOC_CONFIGURE _AUDIOIOC(4)
+#define AUDIOIOC_SHUTDOWN _AUDIOIOC(5)
+#define AUDIOIOC_START _AUDIOIOC(6)
+#define AUDIOIOC_STOP _AUDIOIOC(7)
+#define AUDIOIOC_PAUSE _AUDIOIOC(8)
+#define AUDIOIOC_RESUME _AUDIOIOC(9)
+#define AUDIOIOC_GETBUFFERINFO _AUDIOIOC(10)
+#define AUDIOIOC_ALLOCBUFFER _AUDIOIOC(11)
+#define AUDIOIOC_FREEBUFFER _AUDIOIOC(12)
+#define AUDIOIOC_ENQUEUEBUFFER _AUDIOIOC(13)
+#define AUDIOIOC_REGISTERMQ _AUDIOIOC(14)
+#define AUDIOIOC_UNREGISTERMQ _AUDIOIOC(15)
+#define AUDIOIOC_HWRESET _AUDIOIOC(16)
+
+/* Audio Device Types *******************************************************/
+/* The NuttX audio interface support different types of audio devices for
+ * input, output, synthesis, and manipulation of audio data. A given driver/
+ * device could support a combination of these device type. The following
+ * is a list of bit-field definitions for defining the device type.
+ */
+
+#define AUDIO_TYPE_QUERY 0x00
+#define AUDIO_TYPE_INPUT 0x01
+#define AUDIO_TYPE_OUTPUT 0x02
+#define AUDIO_TYPE_MIXER 0x04
+#define AUDIO_TYPE_SELECTOR 0x08
+#define AUDIO_TYPE_FEATURE 0x10
+#define AUDIO_TYPE_EFFECT 0x20
+#define AUDIO_TYPE_PROCESSING 0x40
+#define AUDIO_TYPE_EXTENSION 0x80
+
+/* Audio Format Types *******************************************************/
+/* The following defines the audio data format types in NuttX. During a
+ * format query, these will be converted to bit positions within the
+ * ac_format field, meaning we currently only support up to 16 formats. To
+ * support more than that, we will use the FMT_OTHER entry, and the
+ * interfacing software can perform a second query to get the other formats.
+ */
+
+#define AUDIO_FMT_UNDEF 0x00
+#define AUDIO_FMT_OTHER 0x01
+#define AUDIO_FMT_MPEG 0x02
+#define AUDIO_FMT_AC3 0x03
+#define AUDIO_FMT_WMA 0x04
+#define AUDIO_FMT_DTS 0x05
+#define AUDIO_FMT_PCM 0x06
+#define AUDIO_FMT_WAV 0x07
+#define AUDIO_FMT_MP3 0x08
+#define AUDIO_FMT_MIDI 0x09
+#define AUDIO_FMT_OGG_VORBIS 0x0a
+#define AUDIO_FMT_FLAC 0x0b
+
+/* Audio Sub-Format Types ***************************************************/
+
+#define AUDIO_SUBFMT_END 0x00
+#define AUDIO_SUBFMT_PCM_MP1 0x01
+#define AUDIO_SUBFMT_PCM_MP2 0x02
+#define AUDIO_SUBFMT_PCM_MP3 0x03
+#define AUDIO_SUBFMT_PCM_MU_LAW 0x04
+#define AUDIO_SUBFMT_PCM_A_LAW 0x05
+#define AUDIO_SUBFMT_PCM_U8 0x06
+#define AUDIO_SUBFMT_PCM_S8 0x07
+#define AUDIO_SUBFMT_PCM_U16_LE 0x08
+#define AUDIO_SUBFMT_PCM_S16_BE 0x09
+#define AUDIO_SUBFMT_PCM_S16_LE 0x0a
+#define AUDIO_SUBFMT_PCM_U16_BE 0x0b
+#define AUDIO_SUBFMT_MIDI_0 0x0c
+#define AUDIO_SUBFMT_MIDI_1 0x0d
+#define AUDIO_SUBFMT_MIDI_2 0x0e
+
+/* Supported Sampling Rates *************************************************/
+
+#define AUDIO_SAMP_RATE_8K 0x0001
+#define AUDIO_SAMP_RATE_11K 0x0002
+#define AUDIO_SAMP_RATE_16K 0x0004
+#define AUDIO_SAMP_RATE_22K 0x0008
+#define AUDIO_SAMP_RATE_32K 0x0010
+#define AUDIO_SAMP_RATE_44K 0x0020
+#define AUDIO_SAMP_RATE_48K 0x0040
+#define AUDIO_SAMP_RATE_96K 0x0080
+#define AUDIO_SAMP_RATE_128K 0x0100
+#define AUDIO_SAMP_RATE_160K 0x0200
+#define AUDIO_SAMP_RATE_172K 0x0400
+#define AUDIO_SAMP_RATE_192K 0x0800
+
+/* Audio Sub-sampling Ratios ***********************************************/
+
+#define AUDIO_SUBSAMPLE_NONE 0
+#define AUDIO_SUBSAMPLE_2X 2
+#define AUDIO_SUBSAMPLE_4X 4
+#define AUDIO_SUBSAMPLE_8X 8
+#define AUDIO_SUBSAMPLE_16X 16
+
+#define AUDIO_SUBSAMPLE_MIN AUDIO_SUBSAMPLE_2X
+#define AUDIO_SUBSAMPLE_MAX AUDIO_SUBSAMPLE_16X
+
+/* Supported Bit Rates *************************************************/
+
+#define AUDIO_BIT_RATE_22K 0x01
+#define AUDIO_BIT_RATE_44K 0x02
+#define AUDIO_BIT_RATE_48K 0x04
+#define AUDIO_BIT_RATE_96K 0x08
+#define AUDIO_BIT_RATE_128K 0x10
+#define AUDIO_BIT_RATE_160K 0x20
+#define AUDIO_BIT_RATE_172K 0x40
+#define AUDIO_BIT_RATE_192K 0x80
+
+/* Supported Feature Units controls *****************************************/
+
+#define AUDIO_FU_UNDEF 0x0000
+#define AUDIO_FU_MUTE 0x0001
+#define AUDIO_FU_VOLUME 0x0002
+#define AUDIO_FU_BASS 0x0004
+#define AUDIO_FU_MID 0x0008
+#define AUDIO_FU_TREBLE 0x0010
+#define AUDIO_FU_EQUALIZER 0x0020
+#define AUDIO_FU_AGC 0x0040
+#define AUDIO_FU_DELAY 0x0080
+#define AUDIO_FU_BASS_BOOST 0x0100
+#define AUDIO_FU_LOUDNESS 0x0200
+#define AUDIO_FU_INP_GAIN 0x0400
+#define AUDIO_FU_BALANCE 0x0800
+#define AUDIO_FU_PHASE_INVERT 0x1000
+#define AUDIO_FU_UNDERFLOW 0x2000
+#define AUDIO_FU_OVERFLOW 0x4000
+#define AUDIO_FU_LATENCY 0x8000
+
+/* Processing Unit controls *************************************************/
+
+#define AUDIO_PU_UNDEF 0x00
+#define AUDIO_PU_UPDOWNMIX 0x01
+#define AUDIO_PU_DOLBY_PROLOGIC 0x02
+#define AUDIO_PU_STEREO_EXTENDER 0x03
+#define AUDIO_PU_SUBSAMPLE_FORWARD 0x04
+#define AUDIO_PU_SUBSAMPLE_REWIND 0x05
+
+/* Stereo Extender PU Controls **********************************************/
+
+#define AUDIO_STEXT_UNDEF 0x00
+#define AUDIO_STEXT_ENABLE 0x01
+#define AUDIO_STEXT_WIDTH 0x02
+#define AUDIO_STEXT_UNDERFLOW 0x03
+#define AUDIO_STEXT_OVERFLOW 0x04
+#define AUDIO_STEXT_LATENCY 0x05
+
+/* Audio Callback Reasons ***************************************************/
+
+#define AUDIO_CALLBACK_UNDEF 0x00
+#define AUDIO_CALLBACK_DEQUEUE 0x01
+#define AUDIO_CALLBACK_IOERR 0x02
+#define AUDIO_CALLBACK_COMPLETE 0x03
+
+/* Audio Pipeline Buffer (AP Buffer) flags **********************************/
+
+#define AUDIO_ABP_ALIGNMENT 0x000f /* Mask to define buffer alignment */
+#define AUDIO_ABP_CANDMA 0x0010 /* Set if the data is DMA'able */
+#define AUDIO_ABP_STATIC 0x0020 /* Set if statically allocated */
+#define AUDIO_ABP_ACTIVE 0x0040 /* Set if this buffer is still active.
+ * A buffer could become inactive
+ * if it is processed by an output
+ * device or a processing device
+ * that replaces it with an alternate
+ * buffer as a result of some DSP
+ * operation, etc.
+ */
+
+/* Standard Audio Message Queue message IDs */
+
+#define AUDIO_MSG_NONE 0
+#define AUDIO_MSG_DEQUEUE 1
+#define AUDIO_MSG_START 2
+#define AUDIO_MSG_STOP 3
+#define AUDIO_MSG_PAUSE 4
+#define AUDIO_MSG_RESUME 5
+#define AUDIO_MSG_DATA_REQUEST 6
+#define AUDIO_MSG_ENQUEUE 7
+#define AUDIO_MSG_COMPLETE 8
+#define AUDIO_MSG_USER 64
+
+/* Audio Pipeline Buffer flags */
+
+#define AUDIO_APB_OUTPUT_ENQUEUED (1 << 0)
+#define AUDIO_APB_OUTPUT_PROCESS (1 << 1)
+#define AUDIO_APB_DEQUEUED (1 << 2)
+#define AUDIO_APB_FINAL (1 << 3) /* Last buffer in the stream */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Define the size of AP Buffer sample count base on CONFIG */
+
+#ifdef CONFIG_AUDIO_LARGE_BUFFERS
+typedef uint32_t apb_samp_t;
+#else
+typedef uint16_t apb_samp_t;
+#endif
+
+/* This structure is used to describe the audio device capabilities */
+
+struct audio_caps_s {
+ uint8_t ac_len; /* Length of the structure */
+ uint8_t ac_type; /* Capabilities (device) type */
+ uint8_t ac_subtype; /* Capabilities sub-type, if needed */
+ uint8_t ac_channels; /* Number of channels (1, 2, 5, 7) */
+
+ union { /* Audio data format(s) for this device */
+ uint8_t b[2];
+ uint16_t hw;
+ } ac_format;
+
+ union {
+ /* Device specific controls. For AUDIO_DEVICE_QUERY, *//* this field reports the device type supported */
+ uint8_t b[4]; /* by this lower-half driver. */
+ uint16_t hw[2];
+ uint32_t w;
+ } ac_controls;
+};
+
+struct audio_caps_desc_s {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ FAR void *session; /* Associated session */
+#endif
+ struct audio_caps_s caps; /* The capabilities struct */
+};
+
+/* This structure describes the characteristics of the Audio samples */
+
+struct audio_info_s {
+ uint8_t samplerate; /* Sample Rate of the audio data */
+ uint8_t channels; /* Number of channels (1, 2, 5, 7) */
+ uint8_t format; /* Audio data format */
+ uint8_t subformat; /* Audio subformat (maybe should be combined with format? */
+};
+
+/* This structure describes the preferred number and size of
+ * audio pipeline buffers for the audio device. Each device
+ * may have unique needs regarding size and qty of buffers,
+ * so this info is queried from the lower-half driver.
+ */
+
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+struct ap_buffer_info_s {
+ apb_samp_t nbuffers; /* Preferred qty of buffers */
+ apb_samp_t buffer_size; /* Preferred size of the buffers */
+};
+#endif
+
+/* This structure describes an Audio Pipeline Buffer */
+
+struct ap_buffer_s {
+ struct dq_entry_s dq_entry; /* Double linked queue entry */
+ struct audio_info_s i; /* The info for samples in this buffer */
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ FAR void *session; /* Associated session */
+#endif
+ apb_samp_t nmaxbytes; /* The maximum number of bytes */
+ apb_samp_t nbytes; /* The number of bytes used */
+ apb_samp_t curbyte; /* Next byte to be processed */
+ sem_t sem; /* Reference locking semaphore */
+ uint16_t flags; /* Buffer flags */
+ uint16_t crefs; /* Number of reference counts */
+ uint8_t samp[0]; /* Offset of the first sample */
+} packed_struct;
+
+/* Structure defining the messages passed to a listening audio thread
+ * for dequeuing buffers and other operations. Also used to allocate
+ * and enqueue buffers via the AUDIOIOC_ALLOCBUFFER, AUDIOIOC_FREEBUFFER,
+ * and AUDIOIOC_ENQUEUEBUFFER ioctls.
+ */
+
+struct audio_msg_s {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ FAR void *session; /* Associated channel */
+#endif
+ uint16_t msgId; /* Message ID */
+ union {
+ FAR void *pPtr; /* Buffer being dequeued */
+ uint32_t data; /* Message data */
+ } u;
+};
+
+/* Structure defining the built-in sounds */
+
+#ifdef CONFIG_AUDIO_BUILTIN_SOUNDS
+struct audio_sound_s {
+ const char *name; /* Name of the sound */
+ uint32_t id; /* ID of the sound */
+ uint32_t type; /* Type of sound */
+ uint32_t size; /* Number of bytes in the sound */
+ const uint8_t *data; /* Pointer to the data */
+};
+
+#endif
+
+/* Structure for allocating, freeing and enqueueing audio pipeline
+ * buffers via the AUDIOIOC_ALLOCBUFFER, AUDIOIOC_FREEBUFFER,
+ * and AUDIOIOC_ENQUEUEBUFFER ioctls.
+ */
+
+struct audio_buf_desc_s {
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ FAR void *session; /* Associated channel */
+#endif
+ uint16_t numbytes; /* Number of bytes to allocate */
+ union {
+ FAR struct ap_buffer_s *pBuffer; /* Buffer to free / enqueue */
+ FAR struct ap_buffer_s **ppBuffer; /* Pointer to receive allocated buffer */
+ } u;
+};
+
+/* Typedef for lower-level to upper-level callback for buffer dequeuing */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+typedef CODE void (*audio_callback_t)(FAR void *priv, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status, FAR void *session);
+#else
+typedef CODE void (*audio_callback_t)(FAR void *priv, uint16_t reason, FAR struct ap_buffer_s *apb, uint16_t status);
+#endif
+
+/* This structure is a set a callback functions used to call from the upper-
+ * half, generic Audo driver into lower-half, platform-specific logic that
+ * supports the low-level functionality.
+ */
+
+struct audio_lowerhalf_s;
+struct audio_ops_s {
+ /* This method is called to retrieve the lower-half device capabilities.
+ * It will be called with device type AUDIO_TYPE_QUERY to request the
+ * overall capabilities, such as to determine the types of devices supported
+ * audio formats supported, etc. Then it may be called once or more with
+ * reported supported device types to determine the specific capabilities
+ * of that device type (such as MP3 encoder, WMA encoder, PCM output, etc.).
+ */
+
+ CODE int (*getcaps)(FAR struct audio_lowerhalf_s *dev, int type, FAR struct audio_caps_s *pCaps);
+
+ /* This method is called to bind the lower-level driver to the upper-level
+ * driver and to configure the driver for a specific mode of
+ * operation defined by the parameters selected in supplied device caps
+ * structure. The lower-level device should perform any initialization
+ * needed to prepare for operations in the specified mode. It should not,
+ * however, process any audio data until the start method is called.
+ */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ CODE int (*configure)(FAR struct audio_lowerhalf_s *dev, FAR void *session, FAR const struct audio_caps_s *pCaps);
+#else
+ CODE int (*configure)(FAR struct audio_lowerhalf_s *dev, FAR const struct audio_caps_s *pCaps);
+#endif
+
+ /* This method is called when the driver is closed. The lower half driver
+ * should stop processing audio data, including terminating any active
+ * output generation. It should also disable the audio hardware and put
+ * it into the lowest possible power usage state.
+ *
+ * Any enqueued Audio Pipeline Buffers that have not been processed / dequeued
+ * should be dequeued by this function.
+ */
+
+ CODE int (*shutdown)(FAR struct audio_lowerhalf_s *dev);
+
+ /* Start audio streaming in the configured mode. For input and synthesis
+ * devices, this means it should begin sending streaming audio data. For output
+ * or processing type device, it means it should begin processing of any enqueued
+ * Audio Pipeline Buffers.
+ */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ CODE int (*start)(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+ CODE int (*start)(FAR struct audio_lowerhalf_s *dev);
+#endif
+
+ /* Stop audio streaming and/or processing of enqueued Audio Pipeline Buffers */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ CODE int (*stop)(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+ CODE int (*stop)(FAR struct audio_lowerhalf_s *dev);
+#endif
+#endif
+
+ /* Pause the audio stream. Should keep current playback context active
+ * in case a resume is issued. Could be called and then followed by a stop.
+ */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ CODE int (*pause)(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+ CODE int (*pause)(FAR struct audio_lowerhalf_s *dev);
+#endif
+
+ /* Resumes audio streaming after a pause */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ CODE int (*resume)(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+ CODE int (*resume)(FAR struct audio_lowerhalf_s *dev);
+#endif
+#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
+
+ /* Allocate an audio pipeline buffer. This routine provides the
+ * lower-half driver with the opportunity to perform special buffer
+ * allocation if needed, such as allocating from a specific memory
+ * region (DMA-able, etc.). If not supplied, then the top-half
+ * driver will perform a standard kumm_malloc using normal user-space
+ * memory region.
+ */
+
+ CODE int (*allocbuffer)(FAR struct audio_lowerhalf_s *dev, FAR struct audio_buf_desc_s *apb);
+
+ /* Free an audio pipeline buffer. If the lower-level driver
+ * provides an allocbuffer routine, it should also provide the
+ * freebuffer routine to perform the free operation.
+ */
+
+ CODE int (*freebuffer)(FAR struct audio_lowerhalf_s *dev, FAR struct audio_buf_desc_s *apb);
+
+ /* Enqueue a buffer for processing. This is a non-blocking enqueue operation.
+ * If the lower-half driver's buffer queue is full, then it should return an
+ * error code of -ENOMEM, and the upper-half driver can decide to either block
+ * the calling thread or deal with it in a non-blocking manner.
+
+ * For each call to enqueuebuffer, the lower-half driver must call
+ * audio_dequeuebuffer when it is finished processing the bufferr, passing the
+ * previously enqueued apb and a dequeue status so that the upper-half driver
+ * can decide if a waiting thread needs to be release, if the dequeued buffer
+ * should be passed to the next block in the Audio Pipeline, etc.
+ */
+
+ CODE int (*enqueuebuffer)(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb);
+
+ /* Cancel a previously enqueued buffer. */
+
+ CODE int (*cancelbuffer)(FAR struct audio_lowerhalf_s *dev, FAR struct ap_buffer_s *apb);
+
+ /* Lower-half logic may support platform-specific ioctl commands */
+
+ CODE int (*ioctl)(FAR struct audio_lowerhalf_s *dev, int cmd, unsigned long arg);
+
+ /* Lower-half logic may support platform-specific read commands */
+
+ CODE int (*read)(FAR struct audio_lowerhalf_s *dev, FAR char *buffer, size_t buflen);
+
+ /* Lower-half logic may support platform-specific write commands */
+
+ CODE int (*write)(FAR struct audio_lowerhalf_s *dev, FAR const char *buffer, size_t buflen);
+
+ /* Reserve a session (may only be one per device or may be multiple) for
+ * use by a client. Client software can open audio devices and issue
+ * AUDIOIOC_GETCAPS calls freely, but other operations require a
+ * reservation. A session reservation will assign a context that must
+ * be passed with
+ */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ CODE int (*reserve)(FAR struct audio_lowerhalf_s *dev, FAR void **psession);
+#else
+ CODE int (*reserve)(FAR struct audio_lowerhalf_s *dev);
+#endif
+
+ /* Release a session. */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+ CODE int (*release)(FAR struct audio_lowerhalf_s *dev, FAR void *session);
+#else
+ CODE int (*release)(FAR struct audio_lowerhalf_s *dev);
+#endif
+};
+
+/* This structure is the generic form of state structure used by lower half
+ * Audio driver. This state structure is passed to the Audio driver when the
+ * driver is initialized. Then, on subsequent callbacks into the lower half
+ * Audio logic, this structure is provided so that the Audio logic can
+ * maintain state information.
+ *
+ * Normally that Audio logic will have its own, custom state structure
+ * that is simply cast to struct audio_lowerhalf_s. In order to perform such
+ * casts, the initial fields of the custom state structure match the initial
+ * fields of the following generic Audio state structure.
+ */
+
+struct audio_lowerhalf_s {
+ /* The first field of this state structure must be a pointer to the Audio
+ * callback structure:
+ */
+
+ FAR const struct audio_ops_s *ops;
+
+ /* The bind data to the upper-half driver used for callbacks of dequeuing
+ * buffer, reporting asynchronous event, reporting errors, etc.
+ */
+
+ FAR audio_callback_t upper;
+
+ /* The private opaque pointer to be passed to upper-layer during callbacks */
+
+ FAR void *priv;
+
+ /* The custom Audio device state structure may include additional fields
+ * after the pointer to the Audio callback structure.
+ */
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * "Upper-Half" Audio Driver Interfaces
+ ****************************************************************************/
+/****************************************************************************
+ * Name: audio_register
+ *
+ * Description:
+ * This function binds an instance of a "lower half" Audio driver with the
+ * "upper half" Audio device and registers that device so that can be used
+ * by application code.
+ *
+ * When this function is called, the "lower half" driver should be in the
+ * reset state (as if the shutdown() method had already been called).
+ *
+ * Input parameters:
+ * name - The name of the audio device. This name will be used to generate
+ * a full path to the driver in the format "/dev/audio/[name]" in the NuttX
+ * filesystem (i.e. the path "/dev/audio" will be prepended to the supplied
+ * device name. The recommended convention is to name Audio drivers
+ * based on the type of functionality they provide, such as "/dev/audio/pcm0",
+ * "/dev/audio/midi0", "/dev/audio/mp30, etc.
+ * dev - A pointer to an instance of lower half audio driver. This instance
+ * is bound to the Audio driver and must persists as long as the driver
+ * persists.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int audio_register(FAR const char *name, FAR struct audio_lowerhalf_s *dev);
+
+/****************************************************************************
+ * Name: abp_alloc
+ *
+ * Description:
+ * Allocated an AP Buffer and prepares it for use. This allocates a dynamically
+ * allocated buffer that has no special DMA capabilities.
+ *
+ * Input parameters:
+ * bufdesc: Pointer to a buffer descriptor
+ *
+ * Returned Value:
+ * Pointer to the allocated buffer or NULL if no memory.
+ *
+ ****************************************************************************/
+
+int apb_alloc(FAR struct audio_buf_desc_s *bufdesc);
+
+/****************************************************************************
+ * Name: apb_free
+ *
+ * Free's a previously allocated or referenced Audio Pipeline Buffer
+ *
+ ****************************************************************************/
+
+void apb_free(FAR struct ap_buffer_s *apb);
+
+/****************************************************************************
+ * Name: apb_reference
+ *
+ * Claim a reference to an Audio Pipeline Buffer. Each call to apb_reference
+ * will increment the reference count and must have a matching apb_free
+ * call. When the refcount decrements to zero, the buffer will be freed.
+ *
+ ****************************************************************************/
+
+void apb_reference(FAR struct ap_buffer_s *apb);
+
+/****************************************************************************
+ * Platform-Dependent "Lower-Half" Audio Driver Interfaces
+ ****************************************************************************/
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_AUDIO */
+#endif /* __INCLUDE_TINYARA_AUDIO_AUDIO_H */
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * include/tinyara/audio/audio_null.h
+ * A do-nothinig audio device driver to simplify testing of audio decoders.
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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_TINYARA_AUDIO_AUDIO_NULL_H
+#define __INCLUDE_TINYARA_AUDIO_AUDIO_NULL_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <tinyara/irq.h>
+
+#ifdef CONFIG_AUDIO_NULL
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************
+ *
+ * CONFIG_AUDIO_NULL - Enabled NULL audio device support
+ * CONFIG_AUDIO_NULL_MSG_PRIO - Priority of messages sent to the NULL audio
+ * device worker thread.
+ * CONFIG_AUDIO_NULL_BUFFER_SIZE - Preferred buffer size
+ * CONFIG_AUDIO_NULL_NUM_BUFFERS - Preferred number of buffers
+ * CONFIG_AUDIO_NULL_WORKER_STACKSIZE - Stack size to use when creating the
+ * NULL audio device worker thread.
+ */
+
+/* Pre-requisites */
+
+#ifndef CONFIG_AUDIO
+#error CONFIG_AUDIO is required for audio subsystem support
+#endif
+
+/* Default configuration values */
+
+#ifndef CONFIG_AUDIO_NULL_MSG_PRIO
+#define CONFIG_AUDIO_NULL_MSG_PRIO 1
+#endif
+
+#ifndef CONFIG_AUDIO_NULL_BUFFER_SIZE
+#define CONFIG_AUDIO_NULL_BUFFER_SIZE 8192
+#endif
+
+#ifndef CONFIG_AUDIO_NULL_NUM_BUFFERS
+#define CONFIG_AUDIO_NULL_NUM_BUFFERS 4
+#endif
+
+#ifndef CONFIG_AUDIO_NULL_WORKER_STACKSIZE
+#define CONFIG_AUDIO_NULL_WORKER_STACKSIZE 768
+#endif
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: audio_null_initialize
+ *
+ * Description:
+ * Initialize the null audio device.
+ *
+ * Input Parameters:
+ * i2c - An I2C driver instance
+ * i2s - An I2S driver instance
+ * lower - Persistent board configuration data
+ *
+ * Returned Value:
+ * A new lower half audio interface for the NULL audio device is returned
+ * on success; NULL is returned on failure.
+ *
+ ****************************************************************************/
+
+struct audio_lowerhalf_s; /* Forward reference. Defined in nuttx/audio/audio.h */
+
+FAR struct audio_lowerhalf_s *audio_null_initialize(void);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_AUDIO_NULL */
+#endif /* __INCLUDE_TINYARA_AUDIO_AUDIO_NULL_H */
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * include/tinyara/audio/i2s.h
+ *
+ * Copyright(C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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_TINYARA_AUDIO_I2S_H
+#define __INCLUDE_TINYARA_AUDIO_I2S_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <tinyara/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <tinyara/audio/audio.h>
+
+#ifdef CONFIG_I2S
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Access macros ************************************************************/
+
+/****************************************************************************
+ * Name: I2S_RXSAMPLERATE
+ *
+ * Description:
+ * Set the I2S RX sample rate. NOTE: This will have no effect if (1) the
+ * driver does not support an I2S receiver or if (2) the sample rate is
+ * driven by the I2S frame clock. This may also have unexpected side-
+ * effects of the RX sample is coupled with the TX sample rate.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * rate - The I2S sample rate in samples (not bits) per second
+ *
+ * Returned Value:
+ * Returns the resulting bitrate
+ *
+ ****************************************************************************/
+
+#define I2S_RXSAMPLERATE(d,f) ((d)->ops->i2s_rxsamplerate(d,r))
+
+/****************************************************************************
+ * Name: I2S_RXDATAWIDTH
+ *
+ * Description:
+ * Set the I2S RX data width. The RX bitrate is determined by
+ * sample_rate * data_width.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * width - The I2S data with in bits.
+ *
+ * Returned Value:
+ * Returns the resulting bitrate
+ *
+ ****************************************************************************/
+
+#define I2S_RXDATAWIDTH(d,b) ((d)->ops->i2s_rxdatawidth(d,b))
+
+/****************************************************************************
+ * Name: I2S_RECEIVE
+ *
+ * Description:
+ * Receive a block of data from I2S.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * apb - A pointer to the audio buffer in which to recieve data
+ * callback - A user provided callback function that will be called at
+ * the completion of the transfer. The callback will be
+ * performed in the context of the worker thread.
+ * arg - An opaque argument that will be provided to the callback
+ * when the transfer complete.
+ * timeout - The timeout value to use. The transfer will be canceled
+ * and an ETIMEDOUT error will be reported if this timeout
+ * elapsed without completion of the DMA transfer. Units
+ * are system clock ticks. Zero means no timeout.
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure. NOTE: This function
+ * only enqueues the transfer and returns immediately. Success here only
+ * means that the transfer was enqueued correctly.
+ *
+ * When the transfer is complete, a 'result' value will be provided as
+ * an argument to the callback function that will indicate if the transfer
+ * failed.
+ *
+ ****************************************************************************/
+
+#define I2S_RECEIVE(d,b,c,a,t) ((d)->ops->i2s_receive(d,b,c,a,t))
+
+/****************************************************************************
+ * Name: I2S_TXSAMPLERATE
+ *
+ * Description:
+ * Set the I2S TX sample rate. NOTE: This will have no effect if (1) the
+ * driver does not support an I2S transmitter or if (2) the sample rate is
+ * driven by the I2S frame clock. This may also have unexpected side-
+ * effects of the TX sample is coupled with the RX sample rate.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * rate - The I2S sample rate in samples (not bits) per second
+ *
+ * Returned Value:
+ * Returns the resulting bitrate
+ *
+ ****************************************************************************/
+
+#define I2S_TXSAMPLERATE(d,f) ((d)->ops->i2s_txsamplerate(d,r))
+
+/****************************************************************************
+ * Name: I2S_TXDATAWIDTH
+ *
+ * Description:
+ * Set the I2S TX data width. The TX bitrate is determined by
+ * sample_rate * data_width.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * width - The I2S data with in bits.
+ *
+ * Returned Value:
+ * Returns the resulting bitrate
+ *
+ ****************************************************************************/
+
+#define I2S_TXDATAWIDTH(d,b) ((d)->ops->i2s_txdatawidth(d,b))
+
+/****************************************************************************
+ * Name: I2S_SEND
+ *
+ * Description:
+ * Send a block of data on I2S.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * apb - A pointer to the audio buffer from which to send data
+ * callback - A user provided callback function that will be called at
+ * the completion of the transfer. The callback will be
+ * performed in the context of the worker thread.
+ * arg - An opaque argument that will be provided to the callback
+ * when the transfer completes.
+ * timeout - The timeout value to use. The transfer will be cancelled
+ * and an ETIMEDOUT error will be reported if this timeout
+ * elapsed without completion of the DMA transfer. Units
+ * are system clock ticks. Zero means no timeout.
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure. NOTE: This function
+ * only enqueues the transfer and returns immediately. Success here only
+ * means that the transfer was enqueued correctly.
+ *
+ * When the transfer is complete, a 'result' value will be provided as
+ * an argument to the callback function that will indicate if the transfer
+ * failed.
+ *
+ ****************************************************************************/
+
+#define I2S_SEND(d,b,c,a,t) ((d)->ops->i2s_send(d,b,c,a,t))
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+/* Transfer complete callbacks */
+
+struct i2s_dev_s;
+typedef CODE void (*i2s_callback_t)(FAR struct i2s_dev_s *dev, FAR struct ap_buffer_s *apb, FAR void *arg, int result);
+
+/* The I2S vtable */
+
+struct i2s_ops_s {
+ /* Receiver methods */
+
+ CODE uint32_t(*i2s_rxsamplerate)(FAR struct i2s_dev_s *dev, uint32_t rate);
+ CODE uint32_t(*i2s_rxdatawidth)(FAR struct i2s_dev_s *dev, int bits);
+ CODE int (*i2s_receive)(FAR struct i2s_dev_s *dev, FAR struct ap_buffer_s *apb, i2s_callback_t callback, FAR void *arg, uint32_t timeout);
+
+ /* Transmitter methods */
+
+ CODE uint32_t(*i2s_txsamplerate)(FAR struct i2s_dev_s *dev, uint32_t rate);
+ CODE uint32_t(*i2s_txdatawidth)(FAR struct i2s_dev_s *dev, int bits);
+ CODE int (*i2s_send)(FAR struct i2s_dev_s *dev, FAR struct ap_buffer_s *apb, i2s_callback_t callback, FAR void *arg, uint32_t timeout);
+};
+
+/* I2S private data. This structure only defines the initial fields of the
+ * structure visible to the I2S client. The specific implementation may
+ * add additional, device specific fields
+ */
+
+struct i2s_dev_s {
+ FAR const struct i2s_ops_s *ops;
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i2schar_register
+ *
+ * Description:
+ * Create and register the I2S character driver.
+ *
+ * The I2S character driver is a simple character driver that supports I2S
+ * transfers via a read() and write(). The intent of this driver is to
+ * support I2S testing. It is not an audio driver but does conform to some
+ * of the buffer management heuristics of an audio driver. It is not
+ * suitable for use in any real driver application in its current form.
+ *
+ * Input Parameters:
+ * i2s - An instance of the lower half I2S driver
+ * minor - The device minor number. The I2S character device will be
+ * registers as /dev/i2scharN where N is the minor number
+ *
+ * Returned Value:
+ * OK if the driver was successfully register; A negated errno value is
+ * returned on any failure.
+ *
+ ****************************************************************************/
+
+int i2schar_register(FAR struct i2s_dev_s *i2s, int minor);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* CONFIG_I2S */
+#endif /* __INCLUDE_TINYARA_AUDIO_I2S_H */
--- /dev/null
+/****************************************************************************
+ *
+ * Copyright 2017 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * include/tinyara/audio/pcm.h
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@tinyara.org>
+ *
+ * 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_TINYARA_AUDIO_PCM_H
+#define __INCLUDE_TINYARA_AUDIO_PCM_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <tinyara/irq.h>
+
+#ifdef CONFIG_AUDIO_FORMAT_PCM
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************
+ *
+ * CONFIG_AUDIO_FORMAT_PCM - Enabled PCM support
+ */
+
+/* Pre-requisites */
+
+#ifndef CONFIG_AUDIO
+#error CONFIG_AUDIO is required for PCM support
+#endif
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+#error CONFIG_SCHED_WORKQUEUE is required by the PCM decoder
+#endif
+
+/* Default configuration values */
+
+/* WAVE Header Definitions **************************************************/
+/* All values are little 32-bit or 16-bit endian */
+
+#define WAV_HDR_CHUNKID 0x46464952 /* "RIFF" */
+#define WAV_HDR_FORMAT 0x45564157 /* "WAVE" */
+#define WAV_FMT_CHUNKID 0x20746d66 /* "fmt " */
+#define WAV_FMT_CHUNKLEN 16 /* Size of a PCM subchunk */
+#define WAV_FMT_FORMAT 1 /* Linear quantization */
+#define WAV_FMT_MONO 1 /* nchannels=1 */
+#define WAV_FMT_STEREO 2 /* nchannels=2 */
+#define WAV_DATA_CHUNKID 0x61746164 /* "data" */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+/* The standard WAV header consist of three chunks
+ *
+ * 1. A WAV header chunk,
+ * 2. A format chunk, and
+ * 3. A data chunk.
+ */
+
+struct wav_hdrchunk_s {
+ uint32_t chunkid; /* Contains the letters "RIFF" in ASCII form. */
+ uint32_t chunklen; /* Size of the rest of the following chunk */
+ uint32_t format; /* Contains the letters "WAVE" */
+};
+
+struct wav_formatchunk_s {
+ uint32_t chunkid; /* Contains the letters "fmt " */
+ uint32_t chunklen; /* Size of the following chunk (16 for PCM) */
+ uint16_t format; /* PCM=1 (i.e. Linear quantization) */
+ uint16_t nchannels; /* Mono=1, Stereo=2 */
+ uint32_t samprate; /* 8000, 44100, ... */
+ uint32_t byterate; /* samprate * nchannels * bpsamp / 8 */
+ uint16_t align; /* nchannels * bpsamp / 8 */
+ uint16_t bpsamp; /* Bits per sample: 8 bits = 8, 16 bits = 16 */
+};
+
+struct wav_datachunk_s {
+ uint32_t chunkid; /* Contains the letters "data" */
+ uint32_t chunklen; /* Number of bytes in the data */
+};
+
+/* The standard WAV file header format is then these three chunks */
+
+struct wav_header_s {
+ struct wav_hdrchunk_s hdr;
+ struct wav_formatchunk_s fmt;
+ struct wav_datachunk_s data;
+};
+
+/****************************************************************************
+* Public Data
+****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pcm_decode_initialize
+ *
+ * Description:
+ * Initialize the PCM device. The PCM device accepts and contains a
+ * low-level audio DAC-type device. It then returns a new audio lower
+ * half interface at adds a PCM decoding from end to the low-level
+ * audio device
+ *
+ * Input Parameters:
+ * dev - A reference to the low-level audio DAC-type device to contain.
+ *
+ * Returned Value:
+ * On success, a new audio device instance is returned that wraps the
+ * low-level device and provides a PCM decoding front end. NULL is
+ * returned on failure.
+ *
+ ****************************************************************************/
+
+FAR struct audio_lowerhalf_s *pcm_decode_initialize(FAR struct audio_lowerhalf_s *dev);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_AUDIO_FORMAT_PCM */
+#endif /* __INCLUDE_TINYARA_AUDIO_PCM_H */
#define _QEIOCVALID(c) (_IOC_TYPE(c) == _QEIOCBASE)
#define _QEIOC(nr) _IOC(_QEIOCBASE, nr)
+/* Audio driver ioctl definitions *************************************/
+/* (see tinyara/audio/audio.h) */
+
+#define _AUDIOIOCVALID(c) (_IOC_TYPE(c)==_AUDIOIOCBASE)
+#define _AUDIOIOC(nr) _IOC(_AUDIOIOCBASE,nr)
+
/* Application Config Data driver ioctl definitions *************************/
/* (see include/tinyara/configdata.h */