Audio devices and audio pipeline buffer support
authorbsvt <b.theogaraj@samsung.com>
Mon, 3 Jul 2017 11:08:56 +0000 (04:08 -0700)
committerShivam Garg <garg.shivam@samsung.com>
Mon, 18 Sep 2017 14:03:31 +0000 (23:03 +0900)
lib/libc/Kconfig
lib/libc/audio/Make.defs
lib/libc/audio/lib_buffer.c
lib/libc/libc.h [new file with mode: 0644]
os/drivers/Kconfig
os/drivers/audio/Kconfig
os/drivers/audio/Make.defs
os/drivers/audio/audio.c [new file with mode: 0644]
os/drivers/audio/i2schar.c
os/include/tinyara/audio/audio.h
os/include/tinyara/fs/ioctl.h

index cfb394f..27009b1 100644 (file)
@@ -691,3 +691,10 @@ config LIB_USRWORKSTACKSIZE
 
 endif # LIB_USRWORK
 endif # BUILD_PROTECTED || BUILD_KERNEL
+
+
+config AUDIO
+       bool "Audio library"
+       default n
+       ---help---
+               Audio library.
index 4c35176..de3b7d5 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.
 #
-###########################################################################
+############################################################################
 ############################################################################
 # libc/audio/Make.defs
 #
@@ -53,7 +53,6 @@
 ifeq ($(CONFIG_AUDIO),y)
 CSRCS += lib_buffer.c
 
-# Add the audio/ directory to the build
 
 DEPPATH += --dep-path audio
 VPATH += :audio
index f8fca68..6f8c768 100644 (file)
 #include <debug.h>
 
 #include <tinyara/audio/audio.h>
-
-#include "lib_internal.h"
-
-#if defined(CONFIG_AUDIO)
+#include "libc.h"
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -135,6 +132,7 @@ int apb_alloc(FAR struct audio_buf_desc_s *bufdesc)
        int ret;
        struct ap_buffer_s *apb;
 
+       DEBUGASSERT(bufdesc != NULL);
        DEBUGASSERT(bufdesc->u.ppBuffer != NULL);
 
        /* Perform a user mode allocation */
@@ -185,7 +183,7 @@ void apb_free(FAR struct ap_buffer_s *apb)
        apb_semgive(apb);
 
        if (refcount <= 1) {
-               audvdbg("Freeing %p\n", apb);
+               audinfo("Freeing %p\n", apb);
                lib_ufree(apb);
        }
 }
@@ -208,4 +206,3 @@ void apb_reference(FAR struct ap_buffer_s *apb)
        apb_semgive(apb);
 }
 
-#endif                                                 /* CONFIG_AUDIO */
diff --git a/lib/libc/libc.h b/lib/libc/libc.h
new file mode 100644 (file)
index 0000000..1a210ab
--- /dev/null
@@ -0,0 +1,143 @@
+
+/****************************************************************************
+ *
+ * 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/libc.h
+ *
+ *   Copyright (C) 2007-2014, 2016-2017 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 __LIB_LIBC_H
+#define __LIB_LIBC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <tinyara/config.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <limits.h>
+#include <semaphore.h>
+
+#include <tinyara/streams.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The Tinyara C library an be build in two modes: (1) as a standard, C-library
+ * that can be used by normal, user-space applications, or (2) as a special,
+ * kernel-mode C-library only used within the OS.  If NuttX is not being
+ * built as separated kernel- and user-space modules, then only the first
+ * mode is supported.
+ */
+
+#if (defined(CONFIG_BUILD_PROTECTED) && defined(__KERNEL__)) || \
+     defined(CONFIG_BUILD_KERNEL)
+#include <tinyara/kmalloc.h>
+
+/* Domain-specific allocations */
+
+#define lib_malloc(s)     kmm_malloc(s)
+#define lib_zalloc(s)     kmm_zalloc(s)
+#define lib_realloc(p,s)  kmm_realloc(p,s)
+#define lib_memalign(p,s) kmm_memalign(p,s)
+#define lib_free(p)       kmm_free(p)
+
+/* User-accessible allocations */
+
+#define lib_umalloc(s)    kumm_malloc(s)
+#define lib_uzalloc(s)    kumm_zalloc(s)
+#define lib_urealloc(p,s) kumm_realloc(p,s)
+#define lib_ufree(p)      kumm_free(p)
+
+#else
+#include <stdlib.h>
+
+/* Domain-specific allocations */
+
+#define lib_malloc(s)     malloc(s)
+#define lib_zalloc(s)     zalloc(s)
+#define lib_realloc(p,s)  realloc(p,s)
+#define lib_free(p)       free(p)
+
+/* User-accessible allocations */
+
+#define lib_umalloc(s)    malloc(s)
+#define lib_uzalloc(s)    zalloc(s)
+#define lib_urealloc(p,s) realloc(p,s)
+#define lib_ufree(p)      free(p)
+
+#endif
+
+#define LIB_BUFLEN_UNKNOWN INT_MAX
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif                                                 /* __LIB_LIBC_H */
index dd2a24e..d2bfb25 100644 (file)
@@ -185,32 +185,6 @@ config GPIO
                driver. See include/tinyara/gpio.h for further GPIO driver
                information.
 
-menuconfig I2S
-       bool "I2S Driver Support"
-       default n
-       ---help---
-               This selection enables selection of common I2S options.  This option
-               should be enabled by all platforms that support I2S interfaces.
-               See include/tinyara/audio/i2s.h for further I2S driver information.
-
-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
@@ -344,17 +318,6 @@ config TIMER
                driver. See include/tinyara/timer.h for further timer driver
                information.
 
-menuconfig I2S
-        bool "I2S Driver Support"
-        default n
-        ---help---
-                This selection enables selection of common I2S options.  This option
-                should be enabled by all platforms that support I2S interfaces.
-                See include/tinyara/audio/i2s.h for further I2S driver information.
-
-if I2S
-endif # I2S
-
 menuconfig ANALOG
        bool "Analog Device(ADC/DAC) Support"
        default n
@@ -368,22 +331,6 @@ if ANALOG
 source drivers/analog/Kconfig
 endif # ANALOG
 
-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 LCD
        bool "LCD Driver Support"
        default n
@@ -462,6 +409,17 @@ if SERIAL
 source drivers/serial/Kconfig
 endif # SERIAL
 
+menuconfig SENSOR
+       bool "Sensor Driver Support"
+       default n
+       ---help---
+               This selection enables building of the sensor driver.
+
+if SENSOR
+source drivers/sensors/Kconfig
+endif # SENSOR
+
+
 menuconfig USBDEV
        bool "USB Device Driver Support"
        default n
@@ -491,12 +449,4 @@ menuconfig DRIVERS_WIRELESS
 
 source drivers/wireless/Kconfig
 
-menuconfig SENSOR
-       bool "Sensor Driver Support"
-       default n
-       ---help---
-               This selection enables building of the sensor driver.
-
-if SENSOR
-source drivers/sensors/Kconfig
-endif
+source drivers/audio/Kconfig
index 65f3c42..35392fe 100644 (file)
@@ -3,6 +3,16 @@
 # see kconfig-language at https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt
 #
 
+menuconfig I2S
+       bool "I2S Driver Support"
+       default n
+       ---help---
+               This selection enables selection of common I2S options.  This option
+               should be enabled by all platforms that support I2S interfaces.
+               See include/tinyara/audio/i2s.h for further I2S driver information.
+
+if I2S
+
 config AUDIO_I2SCHAR
        bool "I2S character driver (for testing only)"
        default n
@@ -34,110 +44,145 @@ config AUDIO_I2SCHAR_TXTIMEOUT
                The special value of zero disables RX timeouts.  Default: 0
 
 endif # AUDIO_I2SCHAR
+endif # I2S
 
-config AUDIO_NULL
-       bool "NULL audio device"
+config AUDIO_DEVICES
+       bool "Support for Audio Devices"
        default n
        depends on AUDIO
        ---help---
-               A do-nothinig audio device driver to simplify testing of audio
-               decoders.
+               Enables support for Audio Devices.
 
-config AUDIO_ALC5658
-       bool "ALC5658 audio chip"
-       depends on AUDIO
+if AUDIO_DEVICES
+
+config AUDIO_MULTI_SESSION
+       bool "Support multiple sessions"
+       default n
        ---help---
-               Select to enable support for the ALC5658 Audio codec by Realtek
-               NOTE: This driver also depends on both I2C and I2S support although
-               that dependency is not explicit here.
+               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.
 
-if AUDIO_ALC5658
+menu "Audio Buffer Configuration"
 
-config ALC5658_INITVOLUME
-       int "ALC5658 initial volume setting"
-       default 250
+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 ALC5658_INFLIGHT
-       int "ALC5658 maximum in-flight audio buffers"
+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 ALC5658_MSG_PRIO
-       int "ALC5658 message priority"
-       default 1
-
-config ALC5658_BUFFER_SIZE
-       int "ALC5658 preferred buffer size"
+config AUDIO_BUFFER_NUMBYTES
+       int "Size of each audio buffer for audio processing"
        default 8192
-
-config ALC5658_NUM_BUFFERS
-       int "ALC5658 preferred number of buffers"
-       default 4
-
-config ALC5658_WORKER_STACKSIZE
-       int "ALC5658 worker thread stack size"
-       default 768
-
-config ALC5658_REGDUMP
-       bool "ALC5658 register dump"
-       default n
        ---help---
-               Enable logic to dump the contents of all ALC5658 registers.
+               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 ALC5658_CLKDEBUG
-       bool "ALC5658 clock analysis"
+config AUDIO_DRIVER_SPECIFIC_BUFFERS
+       bool "Support for Driver specified buffer sizes"
        default n
        ---help---
-               Enable logic to analyze ALC5658 clock configuation.
+               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.
 
-endif # AUDIO_ALC5658
+endmenu # Audio Buffer Configuration
 
-config AUDIO_ALC5658CHAR
-       bool "ALC5658 Character Driver (for testing and demo only)"
-       depends on I2S && AUDIO && I2C && !AUDIO_ALC5658
+menu "Supported Audio Formats"
+
+config AUDIO_FORMAT_PCM
+       bool "PCM Audio"
+       default y
        ---help---
-               This selection enables a simple character driver that supports ALC codec
-               transfers via a read() and write() ioctl.  The intent of this driver is to
-               support ALC codec operation.  It is not an audio driver but does conform to
-               some of the buffer management heuristics of an audio driver.  It can be used
-               as real driver application with come restrictions.
-               form.
+               Build in support for PCM Audio format.
 
-if AUDIO_ALC5658CHAR
+endmenu
 
-config AUDIO_ALC5658CHAR_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
+menu "Exclude Specific Audio Features"
 
-config AUDIO_ALC5658CHAR_TXTIMEOUT
-       int "TX timeout"
-       default 0
+config AUDIO_EXCLUDE_PAUSE_RESUME
+       bool "Exclude pause and resume controls"
+       default n
        ---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
+               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.
 
-endif # AUDIO_ALC5658CHAR
+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_NULL
+if AUDIO_CUSTOM_DEV_PATH
 
-config AUDIO_NULL_MSG_PRIO
-       int "Null audio device message priority"
-       default 1
+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.
 
-config AUDIO_NULL_BUFFER_SIZE
-       int "Null audio device preferred buffer size"
-       default 8192
+if !AUDIO_DEV_ROOT
 
-config AUDIO_NULL_NUM_BUFFERS
-       int "Null audio device preferred number of buffers"
-       default 4
+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.
 
-config AUDIO_NULL_WORKER_STACKSIZE
-       int "Null audio device worker thread stack size"
-       default 768
+endif # AUDIO_CUSTOM_DEV_PATH
+endif #AUDIO_DEV_ROOT
 
-endif # AUDIO_NULL
+endif # AUDIO_DEVICES
index 82fefb9..3789ace 100644 (file)
 # Include Audio drivers
 
 ifeq ($(CONFIG_AUDIO_DEVICES),y)
-
-ifeq ($(CONFIG_AUDIO_NULL),y)
-CSRCS += audio_null.c
-endif
-
+CSRCS += audio.c
 ifeq ($(CONFIG_AUDIO_I2SCHAR),y)
 CSRCS += i2schar.c
 endif
diff --git a/os/drivers/audio/audio.c b/os/drivers/audio/audio.c
new file mode 100644 (file)
index 0000000..e7ee19f
--- /dev/null
@@ -0,0 +1,953 @@
+
+/****************************************************************************
+ *
+ * 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: {
+               auderr("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 tinyara 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) {
+               auderr("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 */
index 0fc20d1..6b3f258 100644 (file)
@@ -88,6 +88,9 @@
 #define CONFIG_AUDIO_I2SCHAR_TXTIMEOUT 0
 #endif
 
+#define i2serr printf
+#define i2sinfo printf
+
 /* Device naming ************************************************************/
 #define DEVNAME_FMT    "/dev/i2schar%d"
 #define DEVNAME_FMTLEN (12 + 3 + 1)
index 7b5f48f..0d68298 100644 (file)
@@ -79,6 +79,8 @@
 
 #ifdef CONFIG_AUDIO
 
+#define audinfo printf
+
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
 #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.
-                                                                                        */
+                                                *   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 */
 
@@ -328,10 +330,10 @@ typedef uint16_t apb_samp_t;
 /* 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) */
+       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];
@@ -339,8 +341,9 @@ struct audio_caps_s {
        } 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. */
+               /* 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;
@@ -381,16 +384,16 @@ 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 */
+       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;
+       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 */
+};
 
 /* Structure defining the messages passed to a listening audio thread
  * for dequeuing buffers and other operations.  Also used to allocate
index 19ac985..1da95bc 100644 (file)
@@ -82,6 +82,7 @@
 #define _CAIOCBASE      (0x0d00)       /* CDC/ACM ioctl commands */
 #define _BATIOCBASE     (0x0e00)       /* Battery driver ioctl commands */
 #define _QEIOCBASE      (0x0f00)       /* Quadrature encoder ioctl commands */
+#define _AUDIOIOCBASE   (0x1000)       /* Audio ioctl commands */
 #define _AUDIOIOCBASE   (0x1000)       /* Audio ioctl commands */
 #define _SLCDIOCBASE    (0x1100)       /* Segment LCD ioctl commands */
 #define _WLIOCBASE      (0x1200)       /* Wireless modules ioctl commands */
 #define _QEIOCVALID(c)    (_IOC_TYPE(c) == _QEIOCBASE)
 #define _QEIOC(nr)        _IOC(_QEIOCBASE, nr)
 
-/* Audio driver ioctl definitions *************************************/
-/* (see tinyara/audio/audio.h) */
+/* Tinyara Audio driver ioctl definitions (see tinyara/audio/audio.h)*************************/
 
-#define _AUDIOIOCVALID(c) (_IOC_TYPE(c) == _AUDIOIOCBASE)
-#define _AUDIOIOC(nr)     _IOC(_AUDIOIOCBASE, nr)
+#define _AUDIOIOCVALID(c) (_IOC_TYPE(c)==_AUDIOIOCBASE)
+#define _AUDIOIOC(nr)     _IOC(_AUDIOIOCBASE,nr)
 
 /* Application Config Data driver ioctl definitions *************************/
 /* (see include/tinyara/configdata.h */