From e119978db25aa17737d46bf05ab4fe08edebad2d Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 29 May 2017 20:15:28 +0900 Subject: [PATCH] audio: import audio framework from NuttX Audio subsystem migrated from Nuttx prior to start defelopment of audio codec and I2S drivers. Change-Id: I442147d5481af28653de5d7e49ca463341a06b0b Signed-off-by: Ivan Signed-off-by: Bongryul Lee Signed-off-by: Junhwan Park --- lib/libc/Makefile | 1 + lib/libc/audio/Make.defs | 60 ++ lib/libc/audio/lib_buffer.c | 211 +++++ os/FlatLibs.mk | 6 + os/Kconfig | 4 + os/LibTargets.mk | 6 + os/audio/Kconfig | 260 +++++++ os/audio/Makefile | 107 +++ os/audio/audio.c | 952 +++++++++++++++++++++++ os/audio/pcm_decode.c | 1373 +++++++++++++++++++++++++++++++++ os/drivers/Kconfig | 15 + os/drivers/Makefile | 1 + os/drivers/audio/Kconfig | 66 ++ os/drivers/audio/Make.defs | 8 +- os/drivers/audio/audio_null.c | 811 +++++++++++++++++++ os/drivers/audio/i2schar.c | 413 ++++++++++ os/include/debug.h | 49 ++ os/include/tinyara/audio/audio.h | 728 +++++++++++++++++ os/include/tinyara/audio/audio_null.h | 152 ++++ os/include/tinyara/audio/i2s.h | 295 +++++++ os/include/tinyara/audio/pcm.h | 181 +++++ os/include/tinyara/fs/ioctl.h | 6 + 22 files changed, 5703 insertions(+), 2 deletions(-) create mode 100755 lib/libc/audio/Make.defs create mode 100755 lib/libc/audio/lib_buffer.c create mode 100755 os/audio/Kconfig create mode 100755 os/audio/Makefile create mode 100644 os/audio/audio.c create mode 100644 os/audio/pcm_decode.c create mode 100755 os/drivers/audio/Kconfig create mode 100644 os/drivers/audio/audio_null.c create mode 100644 os/drivers/audio/i2schar.c create mode 100644 os/include/tinyara/audio/audio.h create mode 100755 os/include/tinyara/audio/audio_null.h create mode 100644 os/include/tinyara/audio/i2s.h create mode 100644 os/include/tinyara/audio/pcm.h diff --git a/lib/libc/Makefile b/lib/libc/Makefile index 2bc6656..b0772ab 100644 --- a/lib/libc/Makefile +++ b/lib/libc/Makefile @@ -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 index 0000000..4c35176 --- /dev/null +++ b/lib/libc/audio/Make.defs @@ -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 +# +# 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 index 0000000..f8fca68 --- /dev/null +++ b/lib/libc/audio/lib_buffer.c @@ -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 + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 */ diff --git a/os/FlatLibs.mk b/os/FlatLibs.mk index 0c08da4..fd59afe 100644 --- a/os/FlatLibs.mk +++ b/os/FlatLibs.mk @@ -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) diff --git a/os/Kconfig b/os/Kconfig index df247cb..cb5686f 100644 --- a/os/Kconfig +++ b/os/Kconfig @@ -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 diff --git a/os/LibTargets.mk b/os/LibTargets.mk index a55675b..b17ad8c 100644 --- a/os/LibTargets.mk +++ b/os/LibTargets.mk @@ -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 index 0000000..31b7f55 --- /dev/null +++ b/os/audio/Kconfig @@ -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 index 0000000..81147e1 --- /dev/null +++ b/os/audio/Makefile @@ -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 +# +# 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 index 0000000..e0fc931 --- /dev/null +++ b/os/audio/audio.c @@ -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 + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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 index 0000000..1369c9c --- /dev/null +++ b/os/audio/pcm_decode.c @@ -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 + * + * Based on the original audio framework from: + * + * Author: Ken Pettit + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 */ diff --git a/os/drivers/Kconfig b/os/drivers/Kconfig index dd9013f..8410f57 100644 --- a/os/drivers/Kconfig +++ b/os/drivers/Kconfig @@ -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 diff --git a/os/drivers/Makefile b/os/drivers/Makefile index 5703fcc..2faf787 100644 --- a/os/drivers/Makefile +++ b/os/drivers/Makefile @@ -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 index 0000000..248d935 --- /dev/null +++ b/os/drivers/audio/Kconfig @@ -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 + diff --git a/os/drivers/audio/Make.defs b/os/drivers/audio/Make.defs index 5af39c1..331d6d3 100644 --- a/os/drivers/audio/Make.defs +++ b/os/drivers/audio/Make.defs @@ -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 @@ -56,6 +56,10 @@ 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 index 0000000..8904773 --- /dev/null +++ b/os/drivers/audio/audio_null.c @@ -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 + * + * 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/**************************************************************************** + * 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 index 0000000..cbc75de --- /dev/null +++ b/os/drivers/audio/i2schar.c @@ -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 + * + * 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 + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/**************************************************************************** + * 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; +} diff --git a/os/include/debug.h b/os/include/debug.h index f94d995..a374bb0 100644 --- a/os/include/debug.h +++ b/os/include/debug.h @@ -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 index 0000000..ca045ff --- /dev/null +++ b/os/include/tinyara/audio/audio.h @@ -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 + * + * 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 +#include + +#include +#include +#include +#include + +#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 index 0000000..cc57b03 --- /dev/null +++ b/os/include/tinyara/audio/audio_null.h @@ -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 + * + * 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 +#include + +#include + +#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 index 0000000..4537748 --- /dev/null +++ b/os/include/tinyara/audio/i2s.h @@ -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 + * + * 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 + +#include +#include +#include + +#include + +#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 index 0000000..42d6948 --- /dev/null +++ b/os/include/tinyara/audio/pcm.h @@ -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 + * + * 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 +#include + +#include + +#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 */ diff --git a/os/include/tinyara/fs/ioctl.h b/os/include/tinyara/fs/ioctl.h index 04dcd58..e45c75d 100644 --- a/os/include/tinyara/fs/ioctl.h +++ b/os/include/tinyara/fs/ioctl.h @@ -292,6 +292,12 @@ #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 */ -- 2.7.4