diff --git a/configs/sama5d4-ek/src/Makefile b/configs/sama5d4-ek/src/Makefile index 0f29ff922c..821edb49ac 100644 --- a/configs/sama5d4-ek/src/Makefile +++ b/configs/sama5d4-ek/src/Makefile @@ -84,6 +84,10 @@ endif endif endif +ifeq ($(CONFIG_AUDIO_NULL),y) +CSRCS += sam_audio_null.c +endif + ifeq ($(CONFIG_SAMA5_HSMCI0),y) CSRCS += sam_hsmci.c else diff --git a/configs/sama5d4-ek/src/sam_audio_null.c b/configs/sama5d4-ek/src/sam_audio_null.c new file mode 100644 index 0000000000..ec9f53b5fe --- /dev/null +++ b/configs/sama5d4-ek/src/sam_audio_null.c @@ -0,0 +1,168 @@ +/************************************************************************************ + * configs/sama5d4-ek/src/sam_audio_null.c + * + * 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 "sama5d4-ek.h" + +#ifdef HAVE_AUDIO_NULL + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_audio_null_initialize + * + * Description: + * Set up to use the NULL audio device for PCM unit-level testing. + * + * Input Parameters: + * minor - The input device minor number + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int sam_audio_null_initialize(int minor) +{ + FAR struct audio_lowerhalf_s *nullaudio; + FAR struct audio_lowerhalf_s *pcm; + static bool initialized = false; + char devname[12]; + int ret; + + auddbg("minor %d\n", minor); + DEBUGASSERT(minor >= 0 && minor <= 25); + + /* Have we already initialized? Since we never uninitialize we must prevent + * multiple initializations. This is necessary, for example, when the + * touchscreen example is used as a built-in application in NSH and can be + * called numerous time. It will attempt to initialize each time. + */ + + if (!initialized) + { + /* Get a null audio interface + */ + + nullaudio = audio_null_initialize(); + if (!nullaudio) + { + auddbg("Failed to get the NULL audio interface\n"); + ret = -ENODEV; + goto errout; + } + + /* No we can embed the null audio interface into a PCM decoder + * instance so that we will have a PCM front end for the NULL + * audio driver. + */ + + pcm = pcm_decode_initialize(nullaudio); + if (!pcm) + { + auddbg("ERROR: Failed create the PCM decoder\n"); + ret = -ENODEV; + goto errout_with_nullaudio; + } + + /* Create a device name */ + + snprintf(devname, 12, "pcm%d", minor); + + /* Finally, we can register the PCM/NULL audio device. */ + + ret = audio_register(devname, pcm); + if (ret < 0) + { + auddbg("ERROR: Failed to register /dev/%s device: %d\n", devname, ret); + goto errout_with_pcm; + } + + /* Now we are initialized */ + + initialized = true; + } + + return OK; + + /* Error exits. Unfortunately there is no mechanism in place now to + * recover resources from most errors on initialization failures. + */ + +errout_with_nullaudio: +errout_with_pcm: +errout: + return ret; +} + +#endif /* HAVE_AUDIO_NULL */ diff --git a/configs/sama5d4-ek/src/sam_nsh.c b/configs/sama5d4-ek/src/sam_nsh.c index 76ccb28245..1c44d273cf 100644 --- a/configs/sama5d4-ek/src/sam_nsh.c +++ b/configs/sama5d4-ek/src/sam_nsh.c @@ -158,7 +158,7 @@ int nsh_archinitialize(void) #endif #ifdef HAVE_WM8904 - /* Start the USB Monitor */ + /* Configure WM8904 audio */ ret = sam_wm8904_initialize(0); if (ret != OK) @@ -167,6 +167,16 @@ int nsh_archinitialize(void) } #endif +#ifdef HAVE_AUDIO_NULL + /* Configure the NULL audio device */ + + ret = sam_audio_null_initialize(0); + if (ret != OK) + { + message("ERROR: Failed to initialize the NULL audio device: %d\n", ret); + } +#endif + /* If we got here then perhaps not all initialization was successful, but * at least enough succeeded to bring-up NSH with perhaps reduced * capabilities. diff --git a/configs/sama5d4-ek/src/sam_wm8904.c b/configs/sama5d4-ek/src/sam_wm8904.c index d10fa38f9c..388bf499a4 100644 --- a/configs/sama5d4-ek/src/sam_wm8904.c +++ b/configs/sama5d4-ek/src/sam_wm8904.c @@ -334,7 +334,7 @@ int sam_wm8904_initialize(int minor) return OK; /* Error exits. Unfortunately there is no mechanism in place now to - * recover from most errors on initialization failures. + * recover resources from most errors on initialization failures. */ errout_with_pcm: diff --git a/configs/sama5d4-ek/src/sama5d4-ek.h b/configs/sama5d4-ek/src/sama5d4-ek.h index 5e6a71d94d..bb9a201306 100644 --- a/configs/sama5d4-ek/src/sama5d4-ek.h +++ b/configs/sama5d4-ek/src/sama5d4-ek.h @@ -65,6 +65,7 @@ #define HAVE_NETWORK 1 #define HAVE_MAXTOUCH 1 #define HAVE_WM8904 1 +#define HAVE_AUDIO_NULL 1 /* HSMCI */ /* Can't support MMC/SD if the card interface(s) are not enable */ @@ -317,7 +318,7 @@ #endif /* Audio */ -/* Default configuration values */ +/* PCM/WM8904 driver */ #ifndef CONFIG_AUDIO_WM8904 # undef HAVE_WM8904 @@ -351,6 +352,23 @@ # endif #endif +/* PCM/null driver */ + +#ifndef CONFIG_AUDIO_NULL +# undef HAVE_AUDIO_NULL +#endif + +#ifdef HAVE_WM8904 +# undef HAVE_AUDIO_NULL +#endif + +#ifdef HAVE_AUDIO_NULL +# ifndef CONFIG_AUDIO_FORMAT_PCM +# warning CONFIG_AUDIO_FORMAT_PCM is required for audio support +# undef HAVE_AUDIO_NULL +# endif +#endif + /* LEDs *****************************************************************************/ /* There are 3 LEDs on the SAMA5D4-EK: * @@ -925,6 +943,25 @@ int nsh_archinitialize(void); int sam_wm8904_initialize(int minor); #endif /* HAVE_WM8904 */ +/**************************************************************************** + * Name: sam_audio_null_initialize + * + * Description: + * Set up to use the NULL audio device for PCM unit-level testing. + * + * Input Parameters: + * minor - The input device minor number + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +#ifdef HAVE_AUDIO_NULL +int sam_audio_null_initialize(int minor); +#endif /* HAVE_AUDIO_NULL */ + #endif /* __ASSEMBLY__ */ #endif /* __CONFIGS_SAMA5D4_EK_SRC_SAMA5D4_EK_H */ diff --git a/drivers/audio/Kconfig b/drivers/audio/Kconfig index c6649750f0..039b8a0ddf 100644 --- a/drivers/audio/Kconfig +++ b/drivers/audio/Kconfig @@ -106,5 +106,57 @@ config AUDIO_WM8904 that dependency is not explicit here. if AUDIO_WM8904 + +config WM8904_INITVOLUME + int "WM8904 initial volume setting" + default 250 + +config WM8904_INFLIGHT + int "WM8904 maximum in-flight audio buffers" + default 2 + +config WM8904_MSG_PRIO + int "WM8904 message priority" + default 1 + +config WM8904_BUFFER_SIZE + int "WM8904 preferred buffer size" + default 8192 + +config WM8904_NUM_BUFFERS + int "WM8904 preferred number of buffers" + default 4 + +config WM8904_WORKER_STACKSIZE + int "WM8904 worker thread stack size" + default 768 + endif # AUDIO_WM8904 +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 UDIO_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/drivers/audio/Make.defs b/drivers/audio/Make.defs index 72bb16b1a3..2b8e978187 100644 --- a/drivers/audio/Make.defs +++ b/drivers/audio/Make.defs @@ -47,6 +47,10 @@ ifeq ($(CONFIG_AUDIO_WM8904),y) CSRCS += wm8904.c endif +ifeq ($(CONFIG_AUDIO_NULL),y) +CSRCS += audio_null.c +endif + ifeq ($(CONFIG_AUDIO_I2SCHAR),y) CSRCS += i2schar.c endif diff --git a/drivers/audio/audio_null.c b/drivers/audio/audio_null.c new file mode 100644 index 0000000000..ea59921de5 --- /dev/null +++ b/drivers/audio/audio_null.c @@ -0,0 +1,764 @@ +/**************************************************************************** + * 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 + +/**************************************************************************** + * Private 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) +{ + audvdbg("Entry\n"); + + /* 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[0] = 0; + caps->ac_format[1] = 0; + caps->ac_controls[0] = 0; + caps->ac_controls[1] = 0; + caps->ac_controls[2] = 0; + caps->ac_controls[3] = 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[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE | + AUDIO_TYPE_PROCESSING; + + break; + + case AUDIO_FMT_MIDI: + /* We only support Format 0 */ + + caps->ac_controls[0] = AUDIO_SUBFMT_END; + break; + + default: + caps->ac_controls[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[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[0] = AUDIO_FU_VOLUME | AUDIO_FU_BASS | AUDIO_FU_TREBLE; + caps->ac_controls[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[0] = AUDIO_PU_STEREO_EXTENDER; + break; + + case AUDIO_PU_STEREO_EXTENDER: + + /* Provide capabilities of our Stereo Extender */ + + caps->ac_controls[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. + */ + + audvdbg("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 +{ + audvdbg("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) +{ + audvdbg("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; + + audvdbg("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, &msg, sizeof(msg), &prio); + + /* Handle the case when we return with no message */ + + if (msglen < sizeof(struct audio_msg_s)) + { + auddbg("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: + auddbg("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 + + audvdbg("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; + + audvdbg("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! */ + + auddbg("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) + { + audvdbg("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); + + audvdbg("Starting worker thread\n"); + ret = pthread_create(&priv->threadid, &tattr, null_workerthread, + (pthread_addr_t)priv); + if (ret != OK) + { + auddbg("ERROR: pthread_create failed: %d\n", ret); + } + else + { + pthread_setname_np(priv->threadid, "null audio"); + audvdbg("Created worker thread\n"); + } + + audvdbg("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 */ + + term_msg.msgId = AUDIO_MSG_STOP; + term_msg.u.data = 0; + mq_send(priv->mq, &term_msg, sizeof(term_msg), CONFIG_AUDIO_NULL_MSG_PRIO); + + /* Join the worker thread */ + + pthread_join(priv->threadid, &value); + priv->threadid = 0; + + audvdbg("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 +{ + audvdbg("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 +{ + audvdbg("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) +{ + /* Take a reference */ + + apb_reference(apb); + + /* say that we consumed all of the data */ + + apb->curbyte = apb->nbytes; + + /* Release the reference and return success */ + + apb_free(apb); + + audvdbg("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) +{ + audvdbg("Return OK\n"); + 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 + + /* 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: + break; + + /* Report our preferred buffer size and quantity */ + +#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS + case AUDIOIOC_GETBUFFERINFO: + 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; + } + + audvdbg("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 +{ + audvdbg("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 *)kzalloc(sizeof(struct null_dev_s)); + if (priv) + { + /* Initialize the null audio device structure. Since we used kzalloc, + * only the non-zero elements of the structure need to be initialized. + */ + + priv->dev.ops = &g_audioops; + return &priv->dev; + } + + return NULL; +} diff --git a/drivers/audio/wm8904.c b/drivers/audio/wm8904.c index d37ef116c8..2a85505d85 100644 --- a/drivers/audio/wm8904.c +++ b/drivers/audio/wm8904.c @@ -95,7 +95,7 @@ struct wm8904_dev_s * "half" that is referred to as "lower". */ - struct audio_lowerhalf_s dev; /* WM8904 audio lower half (this drive) */ + struct audio_lowerhalf_s dev; /* WM8904 audio lower half (this device) */ /* Our specific driver data goes here */ @@ -181,15 +181,10 @@ static int wm8904_stop(FAR struct audio_lowerhalf_s *dev); #ifdef CONFIG_AUDIO_MULTI_SESSION static int wm8904_pause(FAR struct audio_lowerhalf_s *dev, FAR void* session); -#else -static int wm8904_pause(FAR struct audio_lowerhalf_s *dev); -#endif -#endif -#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME -#ifdef CONFIG_AUDIO_MULTI_SESSION static int wm8904_resume(FAR struct audio_lowerhalf_s *dev, FAR void* session); #else +static int wm8904_pause(FAR struct audio_lowerhalf_s *dev); static int wm8904_resume(FAR struct audio_lowerhalf_s *dev); #endif #endif @@ -1755,8 +1750,8 @@ static void wm8904_audio_input(FAR struct wm8904_dev_s *priv) * lower - Persistent board configuration data * * Returned Value: - * A new lower half audio interface is returned for the WM8904 device is - * returned on success; NULL is returned on failure. + * A new lower half audio interface for the WM8904 device is returned on + * success; NULL is returned on failure. * ****************************************************************************/ diff --git a/include/nuttx/audio/audio_null.h b/include/nuttx/audio/audio_null.h new file mode 100644 index 0000000000..24e5bb091f --- /dev/null +++ b/include/nuttx/audio/audio_null.h @@ -0,0 +1,136 @@ +/**************************************************************************** + * include/nuttx/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_NUTTX_AUDIO_AUDIO_NULL_H +#define __INCLUDE_NUTTX_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_NUTTX_AUDIO_AUDIO_NULL_H */ diff --git a/include/nuttx/audio/wm8904.h b/include/nuttx/audio/wm8904.h index 408a41248f..3ce64220c1 100644 --- a/include/nuttx/audio/wm8904.h +++ b/include/nuttx/audio/wm8904.h @@ -93,7 +93,7 @@ /* Default configuration values */ #ifndef CONFIG_WM8904_INITVOLUME -# define CONFIG_WM8904_INITVOLUME 250 +# define CONFIG_WM8904_INITVOLUME 250 #endif #ifndef CONFIG_WM8904_INFLIGHT @@ -199,8 +199,8 @@ extern "C" * lower - Persistent board configuration data * * Returned Value: - * A new lower half audio interface is returned for the WM8904 device is - * returned on success; NULL is returned on failure. + * A new lower half audio interface for the WM8904 device is returned on + * success; NULL is returned on failure. * ****************************************************************************/