audio: import audio framework from NuttX
authorIvan <ivan.galkin@samsung.com>
Mon, 29 May 2017 11:15:28 +0000 (20:15 +0900)
committerIvan Galkin <ivan.galkin@samsung.com>
Sun, 27 Aug 2017 05:27:08 +0000 (14:27 +0900)
Audio subsystem migrated from Nuttx prior to start defelopment of
audio codec and I2S drivers.

Change-Id: I442147d5481af28653de5d7e49ca463341a06b0b
Signed-off-by: Ivan <ivan.galkin@samsung.com>
Signed-off-by: Bongryul Lee <bongryul.lee@samsung.com>
Signed-off-by: Junhwan Park <junhwan.park@samsung.com>
22 files changed:
lib/libc/Makefile
lib/libc/audio/Make.defs [new file with mode: 0755]
lib/libc/audio/lib_buffer.c [new file with mode: 0755]
os/FlatLibs.mk
os/Kconfig
os/LibTargets.mk
os/audio/Kconfig [new file with mode: 0755]
os/audio/Makefile [new file with mode: 0755]
os/audio/audio.c [new file with mode: 0644]
os/audio/pcm_decode.c [new file with mode: 0644]
os/drivers/Kconfig
os/drivers/Makefile
os/drivers/audio/Kconfig [new file with mode: 0755]
os/drivers/audio/Make.defs
os/drivers/audio/audio_null.c [new file with mode: 0644]
os/drivers/audio/i2schar.c [new file with mode: 0644]
os/include/debug.h
os/include/tinyara/audio/audio.h [new file with mode: 0644]
os/include/tinyara/audio/audio_null.h [new file with mode: 0755]
os/include/tinyara/audio/i2s.h [new file with mode: 0644]
os/include/tinyara/audio/pcm.h [new file with mode: 0644]
os/include/tinyara/fs/ioctl.h

index 2bc6656..b0772ab 100644 (file)
@@ -102,6 +102,7 @@ include wqueue/Make.defs
 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 /)
diff --git a/lib/libc/audio/Make.defs b/lib/libc/audio/Make.defs
new file mode 100755 (executable)
index 0000000..4c35176
--- /dev/null
@@ -0,0 +1,60 @@
+###########################################################################
+#
+# 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
diff --git a/lib/libc/audio/lib_buffer.c b/lib/libc/audio/lib_buffer.c
new file mode 100755 (executable)
index 0000000..f8fca68
--- /dev/null
@@ -0,0 +1,211 @@
+/****************************************************************************
+ *
+ * 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 */
index 0c08da4..fd59afe 100644 (file)
@@ -133,6 +133,12 @@ else
 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)
 
index df247cb..cb5686f 100644 (file)
@@ -994,6 +994,10 @@ menu "Networking Support"
 source net/Kconfig
 endmenu
 
+menu "Audio Support"
+source audio/Kconfig
+endmenu
+
 menu "File Systems"
 source fs/Kconfig
 endmenu
index a55675b..b17ad8c 100644 (file)
@@ -105,6 +105,12 @@ fs$(DELIM)libfs$(LIBEXT): context
 $(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)
 
diff --git a/os/audio/Kconfig b/os/audio/Kconfig
new file mode 100755 (executable)
index 0000000..31b7f55
--- /dev/null
@@ -0,0 +1,260 @@
+#
+# 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
diff --git a/os/audio/Makefile b/os/audio/Makefile
new file mode 100755 (executable)
index 0000000..81147e1
--- /dev/null
@@ -0,0 +1,107 @@
+###########################################################################
+#
+# 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
diff --git a/os/audio/audio.c b/os/audio/audio.c
new file mode 100644 (file)
index 0000000..e0fc931
--- /dev/null
@@ -0,0 +1,952 @@
+/****************************************************************************
+ *
+ * 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 */
diff --git a/os/audio/pcm_decode.c b/os/audio/pcm_decode.c
new file mode 100644 (file)
index 0000000..1369c9c
--- /dev/null
@@ -0,0 +1,1373 @@
+/****************************************************************************
+ *
+ * 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 */
index dd9013f..8410f57 100644 (file)
@@ -195,6 +195,21 @@ menuconfig I2S
 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
index 5703fcc..2faf787 100644 (file)
@@ -69,6 +69,7 @@ VPATH = .
 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
diff --git a/os/drivers/audio/Kconfig b/os/drivers/audio/Kconfig
new file mode 100755 (executable)
index 0000000..248d935
--- /dev/null
@@ -0,0 +1,66 @@
+#
+# 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
+
index 5af39c1..331d6d3 100644 (file)
@@ -1,4 +1,4 @@
-############################################################################
+###########################################################################
 #
 # Copyright 2017 Samsung Electronics All Rights Reserved.
 #
@@ -14,7 +14,7 @@
 # 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
diff --git a/os/drivers/audio/audio_null.c b/os/drivers/audio/audio_null.c
new file mode 100644 (file)
index 0000000..8904773
--- /dev/null
@@ -0,0 +1,811 @@
+/****************************************************************************
+ *
+ * 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;
+}
diff --git a/os/drivers/audio/i2schar.c b/os/drivers/audio/i2schar.c
new file mode 100644 (file)
index 0000000..cbc75de
--- /dev/null
@@ -0,0 +1,413 @@
+/****************************************************************************
+ *
+ * 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;
+}
index f94d995..a374bb0 100644 (file)
@@ -592,6 +592,30 @@ Once LOGM is approved, each module should have its own index
 #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__)
@@ -606,6 +630,7 @@ Once LOGM is approved, each module should have its own index
 #define ttdbg(format, ...)
 #endif
 
+
 #else                                                  /* CONFIG_CPP_HAVE_VARARGS */
 
 /* Variadic macros NOT supported */
@@ -934,6 +959,30 @@ Once LOGM is approved, each module should have its own index
 #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 */
diff --git a/os/include/tinyara/audio/audio.h b/os/include/tinyara/audio/audio.h
new file mode 100644 (file)
index 0000000..ca045ff
--- /dev/null
@@ -0,0 +1,728 @@
+/****************************************************************************
+ *
+ * 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 */
diff --git a/os/include/tinyara/audio/audio_null.h b/os/include/tinyara/audio/audio_null.h
new file mode 100755 (executable)
index 0000000..cc57b03
--- /dev/null
@@ -0,0 +1,152 @@
+/****************************************************************************
+ *
+ * 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 */
diff --git a/os/include/tinyara/audio/i2s.h b/os/include/tinyara/audio/i2s.h
new file mode 100644 (file)
index 0000000..4537748
--- /dev/null
@@ -0,0 +1,295 @@
+/****************************************************************************
+ *
+ * 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 */
diff --git a/os/include/tinyara/audio/pcm.h b/os/include/tinyara/audio/pcm.h
new file mode 100644 (file)
index 0000000..42d6948
--- /dev/null
@@ -0,0 +1,181 @@
+/****************************************************************************
+ *
+ * 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 */
index 04dcd58..e45c75d 100644 (file)
 #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 */