This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 374a102af7ba8f52299c49e03db36549e6110ad6 Author: yangyalei <[email protected]> AuthorDate: Mon Jul 21 19:38:03 2025 +0800 nuttx/audio: support multiplexing and state machine management support multiple applications using upper at the same time Signed-off-by: yangyalei <[email protected]> --- audio/audio.c | 359 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 243 insertions(+), 116 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index 9125adbe4e3..aab28fed55a 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -70,20 +70,27 @@ * Private Types ****************************************************************************/ +struct audio_openpriv_s +{ + unsigned long head; + int state; + FAR struct pollfd *fd; + FAR struct audio_openpriv_s *flink; + struct file *usermq; /* User mode app's message queue */ +}; + /* 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 */ struct audio_info_s info; /* Record the last playing audio format */ mutex_t lock; /* Supports mutual exclusion */ spinlock_t spinlock; /* Supports spin lock */ uint8_t periods; /* Ap buffers number */ FAR struct ap_buffer_s **apbs; /* Ap buffers list */ FAR struct audio_lowerhalf_s *dev; /* lower-half state */ - FAR struct pollfd *fd; /* Poll fd */ + FAR struct audio_openpriv_s *head; /* Appl private info list */ struct audio_status_s *status; /* lowerhalf driver status */ - struct file *usermq; /* User mode app's message queue */ }; /**************************************************************************** @@ -151,11 +158,10 @@ 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; + FAR struct audio_openpriv_s *priv; + irqstate_t flags; int ret; - audinfo("crefs: %d\n", upper->crefs); - /* Get exclusive access to the device structures */ ret = nxmutex_lock(&upper->lock); @@ -166,7 +172,7 @@ static int audio_open(FAR struct file *filep) /* First open, alloc status memory */ - if (upper->crefs == 0) + if (upper->head == NULL) { upper->status = kumm_zalloc(sizeof(struct audio_status_s)); if (!upper->status) @@ -177,23 +183,18 @@ static int audio_open(FAR struct file *filep) } } - /* 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) + priv = kmm_zalloc(sizeof(*priv)); + if (priv == NULL) { - /* More than 255 opens; uint8_t overflows to zero */ - - ret = -EMFILE; + ret = -ENOMEM; goto errout; } - /* Save the new open count on success */ - - upper->crefs = tmp; + flags = spin_lock_irqsave(&upper->spinlock); + priv->flink = upper->head; + filep->f_priv = priv; + upper->head = priv; + spin_unlock_irqrestore(&upper->spinlock, flags); ret = OK; errout: @@ -213,10 +214,12 @@ static int audio_close(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct audio_upperhalf_s *upper = inode->i_private; + FAR struct audio_openpriv_s *priv = filep->f_priv; + FAR struct audio_openpriv_s *curr; + FAR struct audio_openpriv_s *prev; + irqstate_t flags; int ret; - audinfo("crefs: %d\n", upper->crefs); - /* Get exclusive access to the device structures */ ret = nxmutex_lock(&upper->lock); @@ -225,21 +228,33 @@ static int audio_close(FAR struct file *filep) return ret; } - /* Decrement the references to the driver. If the reference count will - * decrement to 0, then uninitialize the driver. - */ + flags = spin_lock_irqsave(&upper->spinlock); + + /* find and drop priv from list */ + + for (prev = NULL, curr = upper->head; curr != NULL && curr != priv; + prev = curr, curr = curr->flink); - if (upper->crefs > 1) + if (prev != NULL) { - upper->crefs--; + prev->flink = priv->flink; } else { - FAR struct audio_lowerhalf_s *lower = upper->dev; + upper->head = priv->flink; + } + + spin_unlock_irqrestore(&upper->spinlock, flags); - /* There are no more references to the port */ + kmm_free(priv); - upper->crefs = 0; + /* If the reference head decrement to NULL, + * then uninitialize the driver. + */ + + if (upper->head == NULL) + { + FAR struct audio_lowerhalf_s *lower = upper->dev; /* Disable the Audio device */ @@ -247,7 +262,6 @@ static int audio_close(FAR struct file *filep) audinfo("calling shutdown\n"); lower->ops->shutdown(lower); - upper->usermq = NULL; kumm_free(upper->status); upper->status = NULL; } @@ -332,6 +346,35 @@ static inline void audio_setstate(FAR struct audio_upperhalf_s *upper, spin_unlock_irqrestore(&upper->spinlock, flags); } +/**************************************************************************** + * Name: audio_getnstate + * + * Description: + * Get the applications next top state + * + ****************************************************************************/ + +static inline int audio_getnstate(FAR struct audio_upperhalf_s *upper, + FAR struct audio_openpriv_s *cur) +{ + FAR struct audio_openpriv_s *priv; + int nstate = AUDIO_STATE_OPEN; + irqstate_t flags; + + flags = spin_lock_irqsave(&upper->spinlock); + for (priv = upper->head; priv != NULL; priv = priv->flink) + { + if (priv != cur && priv->state > nstate) + { + nstate = priv->state; + } + } + + spin_unlock_irqrestore(&upper->spinlock, flags); + + return nstate; +} + /**************************************************************************** * Name: audio_configure * @@ -340,33 +383,44 @@ static inline void audio_setstate(FAR struct audio_upperhalf_s *upper, * ****************************************************************************/ -static int audio_configure(FAR struct audio_upperhalf_s *upper, +static int audio_configure(FAR struct file *filep, FAR const struct audio_caps_desc_s *cap_desc) { + 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 const struct audio_caps_s *caps = &cap_desc->caps; + FAR struct audio_openpriv_s *priv = filep->f_priv; int ret = OK; DEBUGASSERT(lower->ops->configure != NULL); + if (upper->status->state == AUDIO_STATE_OPEN || + (caps->ac_type != AUDIO_TYPE_INPUT && + caps->ac_type != AUDIO_TYPE_OUTPUT)) + { #ifdef CONFIG_AUDIO_MULTI_SESSION - ret = lower->ops->configure(lower, cap_desc->session, caps); + ret = lower->ops->configure(lower, cap_desc->session, caps); #else - ret = lower->ops->configure(lower, caps); + ret = lower->ops->configure(lower, caps); #endif - if (ret < 0 || (caps->ac_type != AUDIO_TYPE_INPUT && - caps->ac_type != AUDIO_TYPE_OUTPUT)) - { - return ret; - } + if (ret < 0 || (caps->ac_type != AUDIO_TYPE_INPUT && + caps->ac_type != AUDIO_TYPE_OUTPUT)) + { + return ret; + } + + /* INPUT/OUTPUT configure success here */ - audio_setstate(upper, AUDIO_STATE_PREPARED); - upper->info.format = caps->ac_subtype; - upper->info.channels = caps->ac_channels; - upper->info.subformat = caps->ac_controls.b[2]; - upper->info.samplerate = - caps->ac_controls.hw[0] | (caps->ac_controls.b[3] << 16); + audio_setstate(upper, AUDIO_STATE_PREPARED); + upper->info.format = caps->ac_subtype; + upper->info.channels = caps->ac_channels; + upper->info.subformat = caps->ac_format.b[0]; + upper->info.samplerate = + caps->ac_controls.hw[0] | (caps->ac_controls.b[3] << 16); + } + priv->state = AUDIO_STATE_PREPARED; return ret; } @@ -388,24 +442,31 @@ static int audio_pause(FAR struct file *filep) FAR struct inode *inode = filep->f_inode; FAR struct audio_upperhalf_s *upper = inode->i_private; FAR struct audio_lowerhalf_s *lower = upper->dev; - int ret = OK; + FAR struct audio_openpriv_s *priv = filep->f_priv; + int nstate; + int ret; DEBUGASSERT(upper != NULL && lower->ops->pause != NULL); - if (upper->status->state == AUDIO_STATE_RUNNING) + nstate = audio_getnstate(upper, priv); + if (upper->status->state == AUDIO_STATE_RUNNING && + nstate <= AUDIO_STATE_PAUSED) { #ifdef CONFIG_AUDIO_MULTI_SESSION ret = lower->ops->pause(lower, session); #else ret = lower->ops->pause(lower); #endif - if (ret == OK) + if (ret != OK) { - audio_setstate(upper, AUDIO_STATE_PAUSED); + return ret; } + + audio_setstate(upper, AUDIO_STATE_PAUSED); } - return ret; + priv->state = AUDIO_STATE_PAUSED; + return OK; } #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */ @@ -427,24 +488,29 @@ static int audio_resume(FAR struct file *filep) FAR struct inode *inode = filep->f_inode; FAR struct audio_upperhalf_s *upper = inode->i_private; FAR struct audio_lowerhalf_s *lower = upper->dev; - int ret = OK; + FAR struct audio_openpriv_s *priv = filep->f_priv; + int ret; DEBUGASSERT(upper != NULL && lower->ops->resume != NULL); - if (upper->status->state == AUDIO_STATE_PAUSED) + if (upper->status->state == AUDIO_STATE_PAUSED || + upper->status->state == AUDIO_STATE_XRUN) { #ifdef CONFIG_AUDIO_MULTI_SESSION ret = lower->ops->resume(lower, session); #else ret = lower->ops->resume(lower); #endif - if (ret == OK) + if (ret != OK) { - audio_setstate(upper, AUDIO_STATE_RUNNING); + return ret; } + + audio_setstate(upper, AUDIO_STATE_RUNNING); } - return ret; + priv->state = AUDIO_STATE_RUNNING; + return OK; } #endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */ @@ -465,13 +531,26 @@ static int audio_start(FAR struct file *filep) FAR struct inode *inode = filep->f_inode; FAR struct audio_upperhalf_s *upper = inode->i_private; FAR struct audio_lowerhalf_s *lower = upper->dev; - int ret = OK; + FAR struct audio_openpriv_s *priv = filep->f_priv; + int ret; DEBUGASSERT(upper != NULL && lower->ops->start != NULL); + if (upper->status->state == AUDIO_STATE_OPEN) + { + return -EPERM; + } +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME + else if (upper->status->state == AUDIO_STATE_PAUSED) + { + return audio_resume(filep); + } +#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */ + /* Verify that the Audio is not already running */ - if (upper->status->state == AUDIO_STATE_PREPARED) + if (upper->status->state == AUDIO_STATE_PREPARED || + upper->status->state == AUDIO_STATE_XRUN) { /* Invoke the bottom half method to start the audio stream */ @@ -480,20 +559,20 @@ static int audio_start(FAR struct file *filep) #else ret = lower->ops->start(lower); #endif - - /* A return value of zero means that the audio stream was started + /* A return value of zero means that the audio stream was running * successfully. */ - if (ret == OK) + if (ret != OK) { - /* Indicate that the audio stream has started */ - - audio_setstate(upper, AUDIO_STATE_RUNNING); + return ret; } + + audio_setstate(upper, AUDIO_STATE_RUNNING); } - return ret; + priv->state = AUDIO_STATE_RUNNING; + return OK; } /**************************************************************************** @@ -513,25 +592,49 @@ static int audio_stop(FAR struct file *filep) FAR struct inode *inode = filep->f_inode; FAR struct audio_upperhalf_s *upper = inode->i_private; FAR struct audio_lowerhalf_s *lower = upper->dev; - int ret = OK; + FAR struct audio_openpriv_s *priv = filep->f_priv; + int nstate; + int ret; DEBUGASSERT(upper != NULL && lower->ops->stop != NULL); - if (upper->status->state == AUDIO_STATE_RUNNING || - upper->status->state == AUDIO_STATE_PAUSED) + nstate = audio_getnstate(upper, priv); + if ((upper->status->state == AUDIO_STATE_RUNNING || + upper->status->state == AUDIO_STATE_PAUSED) && + nstate == AUDIO_STATE_OPEN) { #ifdef CONFIG_AUDIO_MULTI_SESSION ret = lower->ops->stop(lower, session); #else ret = lower->ops->stop(lower); #endif - if (ret == OK) + if (ret != OK) + { + return ret; + } + + memset(&upper->info, 0, sizeof(upper->info)); + + /* Audio_complete may have set state to AUDIO_STATE_OPEN */ + + if (upper->status->state != AUDIO_STATE_OPEN) { audio_setstate(upper, AUDIO_STATE_DRAINING); } } +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME + else if (nstate == AUDIO_STATE_PAUSED) + { + ret = audio_pause(filep); + if (ret != OK) + { + return ret; + } + } +#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */ - return ret; + priv->state = AUDIO_STATE_OPEN; + return OK; } /**************************************************************************** @@ -649,6 +752,7 @@ 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_openpriv_s *priv = filep->f_priv; FAR struct audio_buf_desc_s *bufdesc; irqstate_t flags; int ret; @@ -695,7 +799,7 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) /* Call the lower-half driver configure handler */ - ret = audio_configure(upper, caps); + ret = audio_configure(filep, caps); } break; @@ -848,7 +952,7 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { audinfo("AUDIOIOC_REGISTERMQ\n"); - ret = file_get((mqd_t)arg, &upper->usermq); + ret = file_get((mqd_t)arg, &priv->usermq); } break; @@ -861,8 +965,12 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { audinfo("AUDIOIOC_UNREGISTERMQ\n"); - file_put(upper->usermq); - upper->usermq = NULL; + if (priv->usermq != NULL) + { + file_put(priv->usermq); + priv->usermq = NULL; + } + ret = OK; } break; @@ -877,6 +985,11 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) audinfo("AUDIOIOC_RESERVE\n"); DEBUGASSERT(lower->ops->reserve != NULL); + if (upper->head->flink) + { + break; + } + /* Call lower-half to perform the reservation */ #ifdef CONFIG_AUDIO_MULTI_SESSION @@ -897,6 +1010,11 @@ static int audio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) audinfo("AUDIOIOC_RELEASE\n"); DEBUGASSERT(lower->ops->release != NULL); + if (upper->head->flink) + { + break; + } + /* Call lower-half to perform the release */ #ifdef CONFIG_AUDIO_MULTI_SESSION @@ -1023,6 +1141,7 @@ static int audio_poll(FAR struct file *filep, { FAR struct inode *inode = filep->f_inode; FAR struct audio_upperhalf_s *upper = inode->i_private; + FAR struct audio_openpriv_s *priv = filep->f_priv; pollevent_t eventset; irqstate_t flags; @@ -1032,10 +1151,10 @@ static int audio_poll(FAR struct file *filep, if (setup) { - upper->fd = fds; - fds->priv = &upper->fd; + priv->fd = fds; + fds->priv = &priv->fd; - if (upper->hwptr->head - upper->hwptr->tail != upper->periods) + if (priv->head - upper->status->tail != upper->periods) { eventset = POLLIN | POLLOUT; if ((long)(priv->head - upper->status->tail) <= 0) @@ -1059,6 +1178,33 @@ static int audio_poll(FAR struct file *filep, return OK; } +/**************************************************************************** + * Name: audio_sendmsg + * + * Description: + * Send message to the user if a message queue is registered + * + ****************************************************************************/ + +static inline void audio_sendmsg(FAR struct audio_upperhalf_s *upper, + FAR struct audio_msg_s *msg) +{ + FAR struct audio_openpriv_s *priv; + irqstate_t flags; + + flags = spin_lock_irqsave_nopreempt(&upper->spinlock); + for (priv = upper->head; priv != NULL; priv = priv->flink) + { + if (priv->usermq != NULL) + { + file_mq_send(priv->usermq, (FAR const char *)msg, sizeof(*msg), + CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO); + } + } + + spin_unlock_irqrestore_nopreempt(&upper->spinlock, flags); +} + /**************************************************************************** * Name: audio_dequeuebuffer * @@ -1100,6 +1246,7 @@ static inline void audio_dequeuebuffer(FAR struct audio_upperhalf_s *upper, FAR struct ap_buffer_s *apb, uint16_t status) #endif { + FAR struct audio_openpriv_s *priv; struct audio_msg_s msg; pollevent_t eventset; irqstate_t flags; @@ -1108,7 +1255,7 @@ static inline void audio_dequeuebuffer(FAR struct audio_upperhalf_s *upper, flags = spin_lock_irqsave_nopreempt(&upper->spinlock); upper->status->tail++; - if (upper->fd) + for (priv = upper->head; priv != NULL; priv = priv->flink) { if (priv->fd > 0) { @@ -1126,17 +1273,13 @@ static inline void audio_dequeuebuffer(FAR struct audio_upperhalf_s *upper, /* 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; + msg.msg_id = AUDIO_MSG_DEQUEUE; + msg.u.ptr = apb; #ifdef CONFIG_AUDIO_MULTI_SESSION - msg.session = 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); - } + apb->flags |= AUDIO_APB_DEQUEUED; + audio_sendmsg(upper, &msg); } /**************************************************************************** @@ -1169,16 +1312,12 @@ static inline void audio_complete(FAR struct audio_upperhalf_s *upper, /* Send a dequeue message to the user if a message queue is registered */ - if (upper->usermq != NULL) - { - msg.msg_id = AUDIO_MSG_COMPLETE; - msg.u.ptr = NULL; + msg.msg_id = AUDIO_MSG_COMPLETE; + msg.u.ptr = NULL; #ifdef CONFIG_AUDIO_MULTI_SESSION - msg.session = session; + msg.session = session; #endif - file_mq_send(upper->usermq, (FAR const char *)&msg, sizeof(msg), - CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO); - } + audio_sendmsg(upper, &msg); } /**************************************************************************** @@ -1204,14 +1343,10 @@ static inline void audio_message(FAR struct audio_upperhalf_s *upper, /* Send a message to the user if a message queue is registered */ - if (upper->usermq != NULL) - { #ifdef CONFIG_AUDIO_MULTI_SESSION - msg->session = session; + msg->session = session; #endif - file_mq_send(upper->usermq, (FAR const char *)msg, sizeof(*msg), - CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO); - } + audio_sendmsg(upper, msg); } /**************************************************************************** @@ -1239,16 +1374,12 @@ static inline void audio_ioerr(FAR struct audio_upperhalf_s *upper, /* Send io error message to the user if a message queue is registered */ - if (upper->usermq != NULL) - { - msg.msg_id = AUDIO_MSG_IOERR; - msg.u.data = status; + msg.msg_id = AUDIO_MSG_IOERR; + msg.u.data = status; #ifdef CONFIG_AUDIO_MULTI_SESSION - msg.session = session; + msg.session = session; #endif - file_mq_send(upper->usermq, (FAR const char *)&msg, sizeof(msg), - CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO); - } + audio_sendmsg(upper, &msg); } /**************************************************************************** @@ -1276,16 +1407,12 @@ static inline void audio_underrun(FAR struct audio_upperhalf_s *upper, /* Send a dequeue message to the user if a message queue is registered */ - if (upper->usermq != NULL) - { - msg.msg_id = AUDIO_MSG_UNDERRUN; - msg.u.ptr = NULL; + msg.msg_id = AUDIO_MSG_UNDERRUN; + msg.u.ptr = NULL; #ifdef CONFIG_AUDIO_MULTI_SESSION - msg.session = session; + msg.session = session; #endif - file_mq_send(upper->usermq, (FAR const char *)&msg, sizeof(msg), - CONFIG_AUDIO_BUFFER_DEQUEUE_PRIO); - } + audio_sendmsg(upper, &msg); } /****************************************************************************
