12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058 |
- /****************************************************************************
- * audio/audio.c
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership. The
- * ASF licenses this file to you 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.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <fcntl.h>
- #include <assert.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/mqueue.h>
- #include <nuttx/arch.h>
- #include <nuttx/fs/fs.h>
- #include <nuttx/audio/audio.h>
- #include <nuttx/semaphore.h>
- #include <arch/irq.h>
- #ifdef CONFIG_AUDIO
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /* Debug ********************************************************************/
- /* Non-standard debug that may be enabled just for testing Audio */
- #ifndef AUDIO_MAX_DEVICE_PATH
- # define AUDIO_MAX_DEVICE_PATH 32
- #endif
- #ifndef CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO
- # define CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO 1
- #endif
- /****************************************************************************
- * Private Type Definitions
- ****************************************************************************/
- /* This structure describes the state of the upper half driver */
- struct audio_upperhalf_s
- {
- uint8_t crefs; /* The number of times the device has been opened */
- volatile bool started; /* True: playback is active */
- sem_t exclsem; /* Supports mutual exclusion */
- FAR struct audio_lowerhalf_s *dev; /* lower-half state */
- struct file *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 */
- NULL, /* seek */
- audio_ioctl, /* ioctl */
- NULL /* poll */
- };
- /****************************************************************************
- * 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 = nxsem_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;
- ret = OK;
- errout_with_sem:
- nxsem_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 = nxsem_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\n");
- lower->ops->shutdown(lower);
- upper->usermq = NULL;
- }
- ret = OK;
- nxsem_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 = nxsem_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.buffer != NULL);
- apb_free(bufdesc->u.buffer);
- 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.buffer);
- }
- break;
- /* AUDIOIOC_REGISTERMQ - Register a client Message Queue
- *
- * TODO: This needs to have multi session support.
- */
- case AUDIOIOC_REGISTERMQ:
- {
- audinfo("AUDIOIOC_REGISTERMQ\n");
- ret = fs_getfilep((mqd_t)arg, &upper->usermq);
- }
- 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;
- }
- nxsem_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.msg_id = AUDIO_MSG_DEQUEUE;
- msg.u.ptr = apb;
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- msg.session = session;
- #endif
- apb->flags |= AUDIO_APB_DEQUEUED;
- file_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.msg_id = AUDIO_MSG_COMPLETE;
- msg.u.ptr = NULL;
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- msg.session = session;
- #endif
- file_mq_send(upper->usermq, (FAR const char *)&msg, sizeof(msg),
- CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO);
- }
- }
- /****************************************************************************
- * Name: audio_message
- *
- * Description:
- * Send an custom message to the client to indicate that the
- * active message has delivered. 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_message(FAR struct audio_upperhalf_s *upper,
- FAR struct ap_buffer_s *apb, uint16_t status,
- FAR void *session)
- #else
- static inline void audio_message(FAR struct audio_upperhalf_s *upper,
- FAR struct ap_buffer_s *apb, uint16_t status)
- #endif
- {
- struct audio_msg_s *msg = (FAR struct audio_msg_s *)apb;
- /* Send a message to the user if a message queue is registered */
- if (upper->usermq != NULL)
- {
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- msg->session = session;
- #endif
- file_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;
- case AUDIO_CALLBACK_MESSAGE:
- {
- #ifdef CONFIG_AUDIO_MULTI_SESSION
- audio_message(upper, apb, status, session);
- #else
- audio_message(upper, apb, status);
- #endif
- }
- break;
- default:
- {
- auderr("ERROR: Unknown callback reason code %d\n", reason);
- break;
- }
- }
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: audio_register
- *
- * Description:
- * This function binds an instance of a "lower half" audio driver with the
- * "upper half" Audio device and registers that device so that can be used
- * by application code.
- *
- * When this function is called, the "lower half" driver should be in the
- * reset state (as if the shutdown() method had already been called).
- *
- * Input Parameters:
- * path - The full path to the driver to be registers in the 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)
- {
- auderr("ERROR: Allocation failed\n");
- return -ENOMEM;
- }
- /* Initialize the Audio device structure
- * (it was already zeroed by kmm_zalloc())
- */
- nxsem_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 */
|