Check return from nxsem_wait_initialize()

Resolution of Issue 619 will require multiple steps, this part of the first step in that resolution:  Every call to nxsem_wait_uninterruptible() must handle the return value from nxsem_wait_uninterruptible properly.  This commit is only for those files under drivers/audio, drivers/net, and drivers/lcd.
This commit is contained in:
Gregory Nutt 2020-03-31 08:05:48 -06:00 committed by Abdelatif Guettouche
parent 7f510a61b4
commit 5b74974919
9 changed files with 821 additions and 442 deletions

View File

@ -76,17 +76,20 @@ static
uint8_t cs43l22_readreg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr); uint8_t cs43l22_readreg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr);
static void cs43l22_writereg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr, static void cs43l22_writereg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr,
uint8_t regval); uint8_t regval);
static void cs43l22_takesem(sem_t * sem); static int cs43l22_takesem(FAR sem_t *sem);
static int cs43l22_forcetake(FAR sem_t *sem);
#define cs43l22_givesem(s) nxsem_post(s) #define cs43l22_givesem(s) nxsem_post(s)
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
static inline uint16_t cs43l22_scalevolume(uint16_t volume, b16_t scale); static inline uint16_t cs43l22_scalevolume(uint16_t volume, b16_t scale);
static void cs43l22_setvolume(FAR struct cs43l22_dev_s *priv, uint16_t volume, static void cs43l22_setvolume(FAR struct cs43l22_dev_s *priv,
bool mute); uint16_t volume, bool mute);
#endif #endif
#ifndef CONFIG_AUDIO_EXCLUDE_TONE #ifndef CONFIG_AUDIO_EXCLUDE_TONE
static void cs43l22_setbass(FAR struct cs43l22_dev_s *priv, uint8_t bass); static void cs43l22_setbass(FAR struct cs43l22_dev_s *priv,
static void cs43l22_settreble(FAR struct cs43l22_dev_s *priv, uint8_t treble); uint8_t bass);
static void cs43l22_settreble(FAR struct cs43l22_dev_s *priv,
uint8_t treble);
#endif #endif
static void cs43l22_setdatawidth(FAR struct cs43l22_dev_s *priv); static void cs43l22_setdatawidth(FAR struct cs43l22_dev_s *priv);
@ -112,21 +115,25 @@ static void cs43l22_returnbuffers(FAR struct cs43l22_dev_s *priv);
static int cs43l22_sendbuffer(FAR struct cs43l22_dev_s *priv); static int cs43l22_sendbuffer(FAR struct cs43l22_dev_s *priv);
#ifdef CONFIG_AUDIO_MULTI_SESSION #ifdef CONFIG_AUDIO_MULTI_SESSION
static int cs43l22_start(FAR struct audio_lowerhalf_s *dev, FAR void *session); static int cs43l22_start(FAR struct audio_lowerhalf_s *dev,
FAR void *session);
#else #else
static int cs43l22_start(FAR struct audio_lowerhalf_s *dev); static int cs43l22_start(FAR struct audio_lowerhalf_s *dev);
#endif #endif
#ifndef CONFIG_AUDIO_EXCLUDE_STOP #ifndef CONFIG_AUDIO_EXCLUDE_STOP
#ifdef CONFIG_AUDIO_MULTI_SESSION #ifdef CONFIG_AUDIO_MULTI_SESSION
static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session); static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev,
FAR void *session);
#else #else
static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev); static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev);
#endif #endif
#endif #endif
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
#ifdef CONFIG_AUDIO_MULTI_SESSION #ifdef CONFIG_AUDIO_MULTI_SESSION
static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session); static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev,
static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session); FAR void *session);
static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev,
FAR void *session);
#else #else
static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev); static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev);
static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev); static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev);
@ -269,7 +276,6 @@ uint8_t cs43l22_readreg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr)
} }
else else
{ {
/* The I2C transfer was successful... break out of the loop and /* The I2C transfer was successful... break out of the loop and
* return the value read. * return the value read.
*/ */
@ -286,13 +292,13 @@ uint8_t cs43l22_readreg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr)
return 0; return 0;
} }
/************************************************************************************ /****************************************************************************
* Name: cs43l22_writereg * Name: cs43l22_writereg
* *
* Description: * Description:
* Write the specified 16-bit register to the CS43L22 device. * Write the specified 16-bit register to the CS43L22 device.
* *
************************************************************************************/ ****************************************************************************/
static void static void
cs43l22_writereg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr, cs43l22_writereg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr,
@ -355,28 +361,65 @@ cs43l22_writereg(FAR struct cs43l22_dev_s *priv, uint8_t regaddr,
} }
} }
/************************************************************************************ /****************************************************************************
* Name: cs43l22_takesem * Name: cs43l22_takesem
* *
* Description: * Description:
* Take a semaphore count, handling the nasty EINTR return if we are interrupted * Take a semaphore count, handling the nasty EINTR return if we are
* by a signal. * interrupted by a signal.
* *
************************************************************************************/ ****************************************************************************/
static void cs43l22_takesem(sem_t * sem) static int cs43l22_takesem(FAR sem_t *sem)
{ {
nxsem_wait_uninterruptible(sem); return nxsem_wait_uninterruptible(sem);
} }
/************************************************************************************ /****************************************************************************
* Name: cs43l22_forcetake
*
* Description:
* This is just another wrapper but this one continues even if the thread
* is canceled. This must be done in certain conditions where were must
* continue in order to clean-up resources.
*
****************************************************************************/
static int cs43l22_forcetake(FAR sem_t *sem)
{
int result;
int ret = OK;
do
{
result = nxsem_wait_uninterruptible(sem);
/* The only expected error would -ECANCELED meaning that the
* parent thread has been canceled. We have to continue and
* terminate the poll in this case.
*/
DEBUGASSERT(result == OK || result == -ECANCELED);
if (ret == OK && result < 0)
{
/* Remember the first failure */
ret = result;
}
}
while (result < 0);
return ret;
}
/****************************************************************************
* Name: cs43l22_scalevolume * Name: cs43l22_scalevolume
* *
* Description: * Description:
* Set the right and left volume values in the CS43L22 device based on the current * Set the right and left volume values in the CS43L22 device based on the
* volume and balance settings. * current volume and balance settings.
* *
************************************************************************************/ ****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
static inline uint16_t cs43l22_scalevolume(uint16_t volume, b16_t scale) static inline uint16_t cs43l22_scalevolume(uint16_t volume, b16_t scale)
@ -385,14 +428,14 @@ static inline uint16_t cs43l22_scalevolume(uint16_t volume, b16_t scale)
} }
#endif #endif
/************************************************************************************ /****************************************************************************
* Name: cs43l22_setvolume * Name: cs43l22_setvolume
* *
* Description: * Description:
* Set the right and left volume values in the CS43L22 device based on the current * Set the right and left volume values in the CS43L22 device based on the
* volume and balance settings. * current volume and balance settings.
* *
************************************************************************************/ ****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
static void static void
@ -420,7 +463,7 @@ cs43l22_setvolume(FAR struct cs43l22_dev_s *priv, uint16_t volume, bool mute)
leftlevel = ((((1000 - priv->balance) * 100) / 500) * volume) / 100; leftlevel = ((((1000 - priv->balance) * 100) / 500) * volume) / 100;
} }
/* Calculate the right channel volume level {0..1000} */ /* Calculate the right channel volume level {0..1000} */
if (priv->balance >= 500) if (priv->balance >= 500)
{ {
@ -442,10 +485,10 @@ cs43l22_setvolume(FAR struct cs43l22_dev_s *priv, uint16_t volume, bool mute)
/* Set the volume */ /* Set the volume */
regval = (rightlevel + 0x19) & 0xff; regval = (rightlevel + 0x19) & 0xff;
cs43l22_writereg(priv, CS43L22_MS_VOL_CTRL_A, regval); cs43l22_writereg(priv, CS43L22_MS_VOL_CTRL_A, regval);
regval = ((leftlevel + 0x19) & 0xff); regval = ((leftlevel + 0x19) & 0xff);
cs43l22_writereg(priv, CS43L22_MS_VOL_CTRL_B, regval); cs43l22_writereg(priv, CS43L22_MS_VOL_CTRL_B, regval);
#if 0 #if 0
regval = (rightlevel + 0x01) & 0xff; regval = (rightlevel + 0x01) & 0xff;
@ -474,7 +517,7 @@ cs43l22_setvolume(FAR struct cs43l22_dev_s *priv, uint16_t volume, bool mute)
} }
#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */ #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
/************************************************************************************ /****************************************************************************
* Name: cs43l22_setbass * Name: cs43l22_setbass
* *
* Description: * Description:
@ -482,7 +525,7 @@ cs43l22_setvolume(FAR struct cs43l22_dev_s *priv, uint16_t volume, bool mute)
* *
* The level and range are in whole percentage levels (0-100). * The level and range are in whole percentage levels (0-100).
* *
************************************************************************************/ ****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_TONE #ifndef CONFIG_AUDIO_EXCLUDE_TONE
static void cs43l22_setbass(FAR struct cs43l22_dev_s *priv, uint8_t bass) static void cs43l22_setbass(FAR struct cs43l22_dev_s *priv, uint8_t bass)
@ -492,7 +535,7 @@ static void cs43l22_setbass(FAR struct cs43l22_dev_s *priv, uint8_t bass)
} }
#endif /* CONFIG_AUDIO_EXCLUDE_TONE */ #endif /* CONFIG_AUDIO_EXCLUDE_TONE */
/************************************************************************************ /****************************************************************************
* Name: cs43l22_settreble * Name: cs43l22_settreble
* *
* Description: * Description:
@ -500,7 +543,7 @@ static void cs43l22_setbass(FAR struct cs43l22_dev_s *priv, uint8_t bass)
* *
* The level and range are in whole percentage levels (0-100). * The level and range are in whole percentage levels (0-100).
* *
************************************************************************************/ ****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_TONE #ifndef CONFIG_AUDIO_EXCLUDE_TONE
static void cs43l22_settreble(FAR struct cs43l22_dev_s *priv, uint8_t treble) static void cs43l22_settreble(FAR struct cs43l22_dev_s *priv, uint8_t treble)
@ -523,11 +566,13 @@ static void cs43l22_setdatawidth(FAR struct cs43l22_dev_s *priv)
if (priv->bpsamp == 16) if (priv->bpsamp == 16)
{ {
/* Reset default default setting */ /* Reset default default setting */
priv->i2s->ops->i2s_txdatawidth(priv->i2s, 16); priv->i2s->ops->i2s_txdatawidth(priv->i2s, 16);
} }
else else
{ {
/* This should select 8-bit with no companding */ /* This should select 8-bit with no companding */
priv->i2s->ops->i2s_txdatawidth(priv->i2s, 8); priv->i2s->ops->i2s_txdatawidth(priv->i2s, 8);
} }
} }
@ -583,17 +628,20 @@ static int cs43l22_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
switch (caps->ac_subtype) switch (caps->ac_subtype)
{ {
case AUDIO_TYPE_QUERY: case AUDIO_TYPE_QUERY:
/* We don't decode any formats! Only something above us in /* We don't decode any formats! Only something above us in
* the audio stream can perform decoding on our behalf. * the audio stream can perform decoding on our behalf.
*/ */
/* The types of audio units we implement */ /* The types of audio units we implement */
caps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE | caps->ac_controls.b[0] =
AUDIO_TYPE_PROCESSING; AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE |
AUDIO_TYPE_PROCESSING;
break; break;
case AUDIO_FMT_MIDI: case AUDIO_FMT_MIDI:
/* We only support Format 0 */ /* We only support Format 0 */
caps->ac_controls.b[0] = AUDIO_SUBFMT_END; caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
@ -618,10 +666,11 @@ static int cs43l22_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
/* Report the Sample rates we support */ /* Report the Sample rates we support */
caps->ac_controls.b[0] = AUDIO_SAMP_RATE_8K | AUDIO_SAMP_RATE_11K | caps->ac_controls.b[0] =
AUDIO_SAMP_RATE_16K | AUDIO_SAMP_RATE_22K | AUDIO_SAMP_RATE_8K | AUDIO_SAMP_RATE_11K |
AUDIO_SAMP_RATE_32K | AUDIO_SAMP_RATE_44K | AUDIO_SAMP_RATE_16K | AUDIO_SAMP_RATE_22K |
AUDIO_SAMP_RATE_48K; AUDIO_SAMP_RATE_32K | AUDIO_SAMP_RATE_44K |
AUDIO_SAMP_RATE_48K;
break; break;
case AUDIO_FMT_MP3: case AUDIO_FMT_MP3:
@ -639,19 +688,24 @@ static int cs43l22_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
case AUDIO_TYPE_FEATURE: case AUDIO_TYPE_FEATURE:
/* If the sub-type is UNDEF, then report the Feature Units we support */ /* If the sub-type is UNDEF, then report the Feature Units we
* support.
*/
if (caps->ac_subtype == AUDIO_FU_UNDEF) if (caps->ac_subtype == AUDIO_FU_UNDEF)
{ {
/* Fill in the ac_controls section with the Feature Units we have */ /* 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[0] = AUDIO_FU_VOLUME | AUDIO_FU_BASS |
AUDIO_FU_TREBLE;
caps->ac_controls.b[1] = AUDIO_FU_BALANCE >> 8; caps->ac_controls.b[1] = AUDIO_FU_BALANCE >> 8;
} }
else else
{ {
/* TODO: Do we need to provide specific info for the Feature Units, /* TODO: Do we need to provide specific info for the Feature
* such as volume setting ranges, etc.? * Units, such as volume setting ranges, etc.?
*/ */
} }
@ -664,18 +718,22 @@ static int cs43l22_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
switch (caps->ac_subtype) switch (caps->ac_subtype)
{ {
case AUDIO_PU_UNDEF: case AUDIO_PU_UNDEF:
/* Provide the type of Processing Units we support */ /* Provide the type of Processing Units we support */
caps->ac_controls.b[0] = AUDIO_PU_STEREO_EXTENDER; caps->ac_controls.b[0] = AUDIO_PU_STEREO_EXTENDER;
break; break;
case AUDIO_PU_STEREO_EXTENDER: case AUDIO_PU_STEREO_EXTENDER:
/* Provide capabilities of our Stereo Extender */ /* Provide capabilities of our Stereo Extender */
caps->ac_controls.b[0] = AUDIO_STEXT_ENABLE | AUDIO_STEXT_WIDTH; caps->ac_controls.b[0] =
AUDIO_STEXT_ENABLE | AUDIO_STEXT_WIDTH;
break; break;
default: default:
/* Other types of processing uint we don't support */ /* Other types of processing uint we don't support */
break; break;
@ -748,7 +806,8 @@ cs43l22_configure(FAR struct audio_lowerhalf_s *dev,
{ {
/* Scale the volume setting to the range {76..255} */ /* Scale the volume setting to the range {76..255} */
cs43l22_setvolume(priv, (179 * volume / 1000) + 76, priv->mute); cs43l22_setvolume(priv, (179 * volume / 1000) + 76,
priv->mute);
} }
else else
{ {
@ -945,6 +1004,7 @@ cs43l22_senddone(FAR struct i2s_dev_s *i2s,
priv->inflight--; priv->inflight--;
/* Save the result of the transfer */ /* Save the result of the transfer */
/* REVISIT: This can be overwritten */ /* REVISIT: This can be overwritten */
priv->result = result; priv->result = result;
@ -1047,7 +1107,7 @@ static int cs43l22_sendbuffer(FAR struct cs43l22_dev_s *priv)
irqstate_t flags; irqstate_t flags;
uint32_t timeout; uint32_t timeout;
int shift; int shift;
int ret = OK; int ret;
/* Loop while there are audio buffers to be sent and we have few than /* Loop while there are audio buffers to be sent and we have few than
* CONFIG_CS43L22_INFLIGHT then "in-flight" * CONFIG_CS43L22_INFLIGHT then "in-flight"
@ -1061,7 +1121,12 @@ static int cs43l22_sendbuffer(FAR struct cs43l22_dev_s *priv)
* only while accessing 'inflight'. * only while accessing 'inflight'.
*/ */
cs43l22_takesem(&priv->pendsem); ret = cs43l22_takesem(&priv->pendsem);
if (ret < 0)
{
return ret;
}
while (priv->inflight < CONFIG_CS43L22_INFLIGHT && while (priv->inflight < CONFIG_CS43L22_INFLIGHT &&
dq_peek(&priv->pendq) != NULL && !priv->paused) dq_peek(&priv->pendq) != NULL && !priv->paused)
{ {
@ -1128,7 +1193,8 @@ static int cs43l22_sendbuffer(FAR struct cs43l22_dev_s *priv)
****************************************************************************/ ****************************************************************************/
#ifdef CONFIG_AUDIO_MULTI_SESSION #ifdef CONFIG_AUDIO_MULTI_SESSION
static int cs43l22_start(FAR struct audio_lowerhalf_s *dev, FAR void *session) static int cs43l22_start(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
#else #else
static int cs43l22_start(FAR struct audio_lowerhalf_s *dev) static int cs43l22_start(FAR struct audio_lowerhalf_s *dev)
#endif #endif
@ -1143,6 +1209,7 @@ static int cs43l22_start(FAR struct audio_lowerhalf_s *dev)
audinfo("Entry\n"); audinfo("Entry\n");
/* Exit reduced power modes of operation */ /* Exit reduced power modes of operation */
/* REVISIT */ /* REVISIT */
/* Create a message queue for the worker thread */ /* Create a message queue for the worker thread */
@ -1226,6 +1293,7 @@ static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev)
priv->threadid = 0; priv->threadid = 0;
/* Enter into a reduced power usage mode */ /* Enter into a reduced power usage mode */
/* REVISIT: */ /* REVISIT: */
return OK; return OK;
@ -1242,7 +1310,8 @@ static int cs43l22_stop(FAR struct audio_lowerhalf_s *dev)
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
# ifdef CONFIG_AUDIO_MULTI_SESSION # ifdef CONFIG_AUDIO_MULTI_SESSION
static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session) static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
# else # else
static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev) static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev)
# endif # endif
@ -1272,7 +1341,8 @@ static int cs43l22_pause(FAR struct audio_lowerhalf_s *dev)
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
# ifdef CONFIG_AUDIO_MULTI_SESSION # ifdef CONFIG_AUDIO_MULTI_SESSION
static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session) static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
# else # else
static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev) static int cs43l22_resume(FAR struct audio_lowerhalf_s *dev)
# endif # endif
@ -1314,20 +1384,26 @@ static int cs43l22_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
audinfo("Enqueueing: apb=%p curbyte=%d nbytes=%d flags=%04x\n", audinfo("Enqueueing: apb=%p curbyte=%d nbytes=%d flags=%04x\n",
apb, apb->curbyte, apb->nbytes, apb->flags); apb, apb->curbyte, apb->nbytes, apb->flags);
ret = cs43l22_takesem(&priv->pendsem);
if (ret < 0)
{
return ret;
}
/* Take a reference on the new audio buffer */ /* Take a reference on the new audio buffer */
apb_reference(apb); apb_reference(apb);
/* Add the new buffer to the tail of pending audio buffers */ /* Add the new buffer to the tail of pending audio buffers */
cs43l22_takesem(&priv->pendsem);
apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED; apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED;
dq_addlast(&apb->dq_entry, &priv->pendq); dq_addlast(&apb->dq_entry, &priv->pendq);
cs43l22_givesem(&priv->pendsem); cs43l22_givesem(&priv->pendsem);
/* Send a message to the worker thread indicating that a new buffer has been /* Send a message to the worker thread indicating that a new buffer has
* enqueued. If mq is NULL, then the playing has not yet started. In that * been enqueued. If mq is NULL, then the playing has not yet started.
* case we are just "priming the pump" and we don't need to send any message. * In that case we are just "priming the pump" and we don't need to send
* any message.
*/ */
ret = OK; ret = OK;
@ -1437,7 +1513,12 @@ static int cs43l22_reserve(FAR struct audio_lowerhalf_s *dev)
/* Borrow the APBQ semaphore for thread sync */ /* Borrow the APBQ semaphore for thread sync */
cs43l22_takesem(&priv->pendsem); ret = cs43l22_takesem(&priv->pendsem);
if (ret < 0)
{
return ret;
}
if (priv->reserved) if (priv->reserved)
{ {
ret = -EBUSY; ret = -EBUSY;
@ -1472,13 +1553,15 @@ static int cs43l22_reserve(FAR struct audio_lowerhalf_s *dev)
****************************************************************************/ ****************************************************************************/
#ifdef CONFIG_AUDIO_MULTI_SESSION #ifdef CONFIG_AUDIO_MULTI_SESSION
static int cs43l22_release(FAR struct audio_lowerhalf_s *dev, FAR void *session) static int cs43l22_release(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
#else #else
static int cs43l22_release(FAR struct audio_lowerhalf_s *dev) static int cs43l22_release(FAR struct audio_lowerhalf_s *dev)
#endif #endif
{ {
FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev; FAR struct cs43l22_dev_s *priv = (FAR struct cs43l22_dev_s *)dev;
void *value; FAR void *value;
int ret;
/* Join any old worker thread we had created to prevent a memory leak */ /* Join any old worker thread we had created to prevent a memory leak */
@ -1490,14 +1573,14 @@ static int cs43l22_release(FAR struct audio_lowerhalf_s *dev)
/* Borrow the APBQ semaphore for thread sync */ /* Borrow the APBQ semaphore for thread sync */
cs43l22_takesem(&priv->pendsem); ret = cs43l22_forcetake(&priv->pendsem);
/* Really we should free any queued buffers here */ /* Really we should free any queued buffers here */
priv->reserved = false; priv->reserved = false;
cs43l22_givesem(&priv->pendsem); cs43l22_givesem(&priv->pendsem);
return OK; return ret;
} }
/**************************************************************************** /****************************************************************************
@ -1623,6 +1706,7 @@ static void *cs43l22_workerthread(pthread_addr_t pvarg)
#ifndef CONFIG_AUDIO_EXCLUDE_STOP #ifndef CONFIG_AUDIO_EXCLUDE_STOP
case AUDIO_MSG_STOP: case AUDIO_MSG_STOP:
/* Indicate that we are terminating */ /* Indicate that we are terminating */
audinfo("AUDIO_MSG_STOP: Terminating\n"); audinfo("AUDIO_MSG_STOP: Terminating\n");
@ -1657,7 +1741,7 @@ static void *cs43l22_workerthread(pthread_addr_t pvarg)
/* Return any pending buffers in our pending queue */ /* Return any pending buffers in our pending queue */
cs43l22_takesem(&priv->pendsem); cs43l22_forcetake(&priv->pendsem);
while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL) while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL)
{ {
/* Release our reference to the buffer */ /* Release our reference to the buffer */
@ -1722,7 +1806,8 @@ static void cs43l22_audio_output(FAR struct cs43l22_dev_s *priv)
/* SPK always off and HP always on */ /* SPK always off and HP always on */
regval = CS43L22_PDN_HPB_ON | CS43L22_PDN_HPA_ON | CS43L22_PDN_SPKB_OFF | CS43L22_PDN_SPKA_OFF; regval = CS43L22_PDN_HPB_ON | CS43L22_PDN_HPA_ON | CS43L22_PDN_SPKB_OFF |
CS43L22_PDN_SPKA_OFF;
cs43l22_writereg(priv, CS43L22_POWER_CTRL2, regval); cs43l22_writereg(priv, CS43L22_POWER_CTRL2, regval);
/* Clock configuration: Auto detection */ /* Clock configuration: Auto detection */
@ -1841,7 +1926,7 @@ static void cs43l22_reset(FAR struct cs43l22_dev_s *priv)
priv->nchannels = CS43L22_DEFAULT_NCHANNELS; priv->nchannels = CS43L22_DEFAULT_NCHANNELS;
priv->bpsamp = CS43L22_DEFAULT_BPSAMP; priv->bpsamp = CS43L22_DEFAULT_BPSAMP;
#if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) && !defined(CONFIG_AUDIO_EXCLUDE_BALANCE) #if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) && !defined(CONFIG_AUDIO_EXCLUDE_BALANCE)
priv->balance = 500; // b16HALF; /* Center balance */ priv->balance = 500; /* b16HALF = Center balance */
#endif #endif
/* Software reset. This puts all CS43L22 registers back in their /* Software reset. This puts all CS43L22 registers back in their
@ -1889,10 +1974,9 @@ static void cs43l22_reset(FAR struct cs43l22_dev_s *priv)
* *
****************************************************************************/ ****************************************************************************/
FAR struct audio_lowerhalf_s *cs43l22_initialize(FAR struct i2c_master_s *i2c, FAR struct audio_lowerhalf_s *
FAR struct i2s_dev_s *i2s, cs43l22_initialize(FAR struct i2c_master_s *i2c, FAR struct i2s_dev_s *i2s,
FAR const struct FAR const struct cs43l22_lower_s *lower)
cs43l22_lower_s *lower)
{ {
FAR struct cs43l22_dev_s *priv; FAR struct cs43l22_dev_s *priv;
uint16_t regval; uint16_t regval;
@ -1903,7 +1987,8 @@ FAR struct audio_lowerhalf_s *cs43l22_initialize(FAR struct i2c_master_s *i2c,
/* Allocate a CS43L22 device structure */ /* Allocate a CS43L22 device structure */
priv = (FAR struct cs43l22_dev_s *)kmm_zalloc(sizeof(struct cs43l22_dev_s)); priv = (FAR struct cs43l22_dev_s *)
kmm_zalloc(sizeof(struct cs43l22_dev_s));
if (priv) if (priv)
{ {
/* Initialize the CS43L22 device structure. Since we used kmm_zalloc, /* Initialize the CS43L22 device structure. Since we used kmm_zalloc,
@ -1921,10 +2006,12 @@ FAR struct audio_lowerhalf_s *cs43l22_initialize(FAR struct i2c_master_s *i2c,
/* Initialize I2C */ /* Initialize I2C */
audinfo("address=%02x frequency=%d\n", lower->address, lower->frequency); audinfo("address=%02x frequency=%d\n",
lower->address, lower->frequency);
/* Software reset. This puts all CS43L22 registers back in their default /* Software reset. This puts all CS43L22 registers back in their
* state. */ * default state.
*/
CS43L22_HW_RESET(priv->lower); CS43L22_HW_RESET(priv->lower);

View File

@ -74,7 +74,8 @@
static void wm8776_writereg(FAR struct wm8776_dev_s *priv, static void wm8776_writereg(FAR struct wm8776_dev_s *priv,
uint8_t regaddr, uint16_t regval); uint8_t regaddr, uint16_t regval);
static void wm8776_takesem(sem_t *sem); static int wm8776_takesem(FAR sem_t *sem);
static int wm8776_forcetake(FAR sem_t *sem);
#define wm8776_givesem(s) nxsem_post(s) #define wm8776_givesem(s) nxsem_post(s)
static int wm8776_getcaps(FAR struct audio_lowerhalf_s *dev, int type, static int wm8776_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
@ -174,13 +175,13 @@ static const struct audio_ops_s g_audioops =
wm8776_release /* release */ wm8776_release /* release */
}; };
/************************************************************************************ /****************************************************************************
* Name: wm8776_writereg * Name: wm8776_writereg
* *
* Description: * Description:
* Write the specified 16-bit register to the WM8776 device. * Write the specified 16-bit register to the WM8776 device.
* *
************************************************************************************/ ****************************************************************************/
static void wm8776_writereg(FAR struct wm8776_dev_s *priv, static void wm8776_writereg(FAR struct wm8776_dev_s *priv,
uint8_t regaddr, uint8_t regaddr,
@ -208,28 +209,65 @@ static void wm8776_writereg(FAR struct wm8776_dev_s *priv,
} }
} }
/************************************************************************************ /****************************************************************************
* Name: wm8776_takesem * Name: wm8776_takesem
* *
* Description: * Description:
* Take a semaphore count, handling the nasty EINTR return if we are interrupted * Take a semaphore count, handling the nasty EINTR return if we are
* by a signal. * interrupted by a signal.
* *
************************************************************************************/ ****************************************************************************/
static void wm8776_takesem(sem_t *sem) static int wm8776_takesem(sem_t *sem)
{ {
nxsem_wait_uninterruptible(sem); return nxsem_wait_uninterruptible(sem);
} }
/************************************************************************************ /****************************************************************************
* Name: wm8776_forcetake
*
* Description:
* This is just another wrapper but this one continues even if the thread
* is canceled. This must be done in certain conditions where were must
* continue in order to clean-up resources.
*
****************************************************************************/
static int wm8776_forcetake(FAR sem_t *sem)
{
int result;
int ret = OK;
do
{
result = nxsem_wait_uninterruptible(sem);
/* The only expected error would -ECANCELED meaning that the
* parent thread has been canceled. We have to continue and
* terminate the poll in this case.
*/
DEBUGASSERT(result == OK || result == -ECANCELED);
if (ret == OK && result < 0)
{
/* Remember the first failure */
ret = result;
}
}
while (result < 0);
return ret;
}
/****************************************************************************
* Name: wm8776_setvolume * Name: wm8776_setvolume
* *
* Description: * Description:
* Set the right and left volume values in the WM8776 device based on the current * Set the right and left volume values in the WM8776 device based on the
* volume and balance settings. * current volume and balance settings.
* *
************************************************************************************/ ****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
static void wm8776_setvolume(FAR struct wm8776_dev_s *priv, uint16_t volume, static void wm8776_setvolume(FAR struct wm8776_dev_s *priv, uint16_t volume,
@ -312,8 +350,9 @@ static int wm8776_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
/* The types of audio units we implement */ /* The types of audio units we implement */
caps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE | caps->ac_controls.b[0] =
AUDIO_TYPE_PROCESSING; AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE |
AUDIO_TYPE_PROCESSING;
break; break;
@ -413,13 +452,14 @@ static int wm8776_configure(FAR struct audio_lowerhalf_s *dev,
{ {
/* Scale the volume setting to the range {0x2f .. 0x79} */ /* Scale the volume setting to the range {0x2f .. 0x79} */
wm8776_setvolume(priv, (0x4a * volume / 1000) + 0x2f, priv->mute); wm8776_setvolume(priv, (0x4a * volume / 1000) + 0x2f,
priv->mute);
} }
else else
{ {
ret = -EDOM; ret = -EDOM;
} }
} }
break; break;
#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */ #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
@ -537,6 +577,7 @@ static void wm8776_senddone(FAR struct i2s_dev_s *i2s,
priv->inflight--; priv->inflight--;
/* Save the result of the transfer */ /* Save the result of the transfer */
/* REVISIT: This can be overwritten */ /* REVISIT: This can be overwritten */
priv->result = result; priv->result = result;
@ -639,7 +680,7 @@ static int wm8776_sendbuffer(FAR struct wm8776_dev_s *priv)
irqstate_t flags; irqstate_t flags;
uint32_t timeout; uint32_t timeout;
int shift; int shift;
int ret = OK; int ret;
/* Loop while there are audio buffers to be sent and we have few than /* Loop while there are audio buffers to be sent and we have few than
* CONFIG_WM8776_INFLIGHT then "in-flight" * CONFIG_WM8776_INFLIGHT then "in-flight"
@ -653,7 +694,12 @@ static int wm8776_sendbuffer(FAR struct wm8776_dev_s *priv)
* only while accessing 'inflight'. * only while accessing 'inflight'.
*/ */
wm8776_takesem(&priv->pendsem); ret = wm8776_takesem(&priv->pendsem);
if (ret < 0)
{
return ret;
}
while (priv->inflight < CONFIG_WM8776_INFLIGHT && while (priv->inflight < CONFIG_WM8776_INFLIGHT &&
dq_peek(&priv->pendq) != NULL && !priv->paused) dq_peek(&priv->pendq) != NULL && !priv->paused)
{ {
@ -713,6 +759,7 @@ static int wm8776_start(FAR struct audio_lowerhalf_s *dev)
audinfo("Entry\n"); audinfo("Entry\n");
/* Exit reduced power modes of operation */ /* Exit reduced power modes of operation */
/* REVISIT */ /* REVISIT */
/* Create a message queue for the worker thread */ /* Create a message queue for the worker thread */
@ -796,6 +843,7 @@ static int wm8776_stop(FAR struct audio_lowerhalf_s *dev)
priv->threadid = 0; priv->threadid = 0;
/* Enter into a reduced power usage mode */ /* Enter into a reduced power usage mode */
/* REVISIT: */ /* REVISIT: */
return OK; return OK;
@ -837,7 +885,8 @@ static int wm8776_pause(FAR struct audio_lowerhalf_s *dev)
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
#ifdef CONFIG_AUDIO_MULTI_SESSION #ifdef CONFIG_AUDIO_MULTI_SESSION
static int wm8776_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session) static int wm8776_resume(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
#else #else
static int wm8776_resume(FAR struct audio_lowerhalf_s *dev) static int wm8776_resume(FAR struct audio_lowerhalf_s *dev)
#endif #endif
@ -878,14 +927,20 @@ static int wm8776_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
/* Add the new buffer to the tail of pending audio buffers */ /* Add the new buffer to the tail of pending audio buffers */
wm8776_takesem(&priv->pendsem); ret = wm8776_takesem(&priv->pendsem);
if (ret < 0)
{
return ret;
}
apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED; apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED;
dq_addlast(&apb->dq_entry, &priv->pendq); dq_addlast(&apb->dq_entry, &priv->pendq);
wm8776_givesem(&priv->pendsem); wm8776_givesem(&priv->pendsem);
/* Send a message to the worker thread indicating that a new buffer has been /* Send a message to the worker thread indicating that a new buffer has
* enqueued. If mq is NULL, then the playing has not yet started. In that * been enqueued. If mq is NULL, then the playing has not yet started.
* case we are just "priming the pump" and we don't need to send any message. * In that case we are just "priming the pump" and we don't need to send
* any message.
*/ */
ret = OK; ret = OK;
@ -996,7 +1051,12 @@ static int wm8776_reserve(FAR struct audio_lowerhalf_s *dev)
/* Borrow the APBQ semaphore for thread sync */ /* Borrow the APBQ semaphore for thread sync */
wm8776_takesem(&priv->pendsem); ret = wm8776_takesem(&priv->pendsem);
if (ret < 0)
{
return ret;
}
if (priv->reserved) if (priv->reserved)
{ {
ret = -EBUSY; ret = -EBUSY;
@ -1037,7 +1097,8 @@ static int wm8776_release(FAR struct audio_lowerhalf_s *dev)
#endif #endif
{ {
FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev; FAR struct wm8776_dev_s *priv = (FAR struct wm8776_dev_s *)dev;
void *value; FAR void *value;
int ret;
/* Join any old worker thread we had created to prevent a memory leak */ /* Join any old worker thread we had created to prevent a memory leak */
@ -1049,14 +1110,14 @@ static int wm8776_release(FAR struct audio_lowerhalf_s *dev)
/* Borrow the APBQ semaphore for thread sync */ /* Borrow the APBQ semaphore for thread sync */
wm8776_takesem(&priv->pendsem); ret = wm8776_forcetake(&priv->pendsem);
/* Really we should free any queued buffers here */ /* Really we should free any queued buffers here */
priv->reserved = false; priv->reserved = false;
wm8776_givesem(&priv->pendsem); wm8776_givesem(&priv->pendsem);
return OK; return ret;
} }
/**************************************************************************** /****************************************************************************
@ -1075,11 +1136,12 @@ static int wm8776_release(FAR struct audio_lowerhalf_s *dev)
static void wm8776_audio_output(FAR struct wm8776_dev_s *priv) static void wm8776_audio_output(FAR struct wm8776_dev_s *priv)
{ {
wm8776_writereg(priv, WM8776_MASTER_ATT, WM8776_UPDATE | 0x58); /* -33db */ wm8776_writereg(priv, WM8776_MASTER_ATT,
wm8776_writereg(priv, WM8776_DAC_IF, 0x32); /* 32bit, I2S, standard pol */ WM8776_UPDATE | 0x58); /* -33db */
wm8776_writereg(priv, WM8776_DAC_IF, 0x32); /* 32bit, I2S, standard pol */
#ifdef CONFIG_WM8776_SWAP_HPOUT #ifdef CONFIG_WM8776_SWAP_HPOUT
wm8776_writereg(priv, WM8776_DAC_CC, 0x62); /* Swap HPOUT L/R */ wm8776_writereg(priv, WM8776_DAC_CC, 0x62); /* Swap HPOUT L/R */
#endif #endif
wm8776_writereg(priv, WM8776_MASTER_MODE, 0x00); /* slave mode, 128fs */ wm8776_writereg(priv, WM8776_MASTER_MODE, 0x00); /* slave mode, 128fs */
@ -1120,7 +1182,6 @@ static void wm8776_hw_reset(FAR struct wm8776_dev_s *priv)
/* Configure the WM8776 hardware as an audio input device */ /* Configure the WM8776 hardware as an audio input device */
wm8776_audio_output(priv); wm8776_audio_output(priv);
} }
/**************************************************************************** /****************************************************************************
@ -1203,6 +1264,7 @@ repeat:
#ifndef CONFIG_AUDIO_EXCLUDE_STOP #ifndef CONFIG_AUDIO_EXCLUDE_STOP
case AUDIO_MSG_STOP: case AUDIO_MSG_STOP:
/* Indicate that we are terminating */ /* Indicate that we are terminating */
audinfo("AUDIO_MSG_STOP: Terminating\n"); audinfo("AUDIO_MSG_STOP: Terminating\n");
@ -1238,7 +1300,6 @@ repeat:
{ {
goto repeat; goto repeat;
} }
} }
/* Reset the WM8776 hardware */ /* Reset the WM8776 hardware */
@ -1247,7 +1308,7 @@ repeat:
/* Return any pending buffers in our pending queue */ /* Return any pending buffers in our pending queue */
wm8776_takesem(&priv->pendsem); wm8776_forcetake(&priv->pendsem);
while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL) while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL)
{ {
/* Release our reference to the buffer */ /* Release our reference to the buffer */
@ -1287,7 +1348,6 @@ repeat:
return NULL; return NULL;
} }
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/

View File

@ -94,7 +94,8 @@ static
uint8_t regaddr); uint8_t regaddr);
static void wm8904_writereg(FAR struct wm8904_dev_s *priv, static void wm8904_writereg(FAR struct wm8904_dev_s *priv,
uint8_t regaddr, uint16_t regval); uint8_t regaddr, uint16_t regval);
static void wm8904_takesem(sem_t *sem); static int wm8904_takesem(FAR sem_t *sem);
static int wm8904_forcetake(FAR sem_t *sem);
#define wm8904_givesem(s) nxsem_post(s) #define wm8904_givesem(s) nxsem_post(s)
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
@ -104,7 +105,8 @@ static void wm8904_setvolume(FAR struct wm8904_dev_s *priv,
#endif #endif
#ifndef CONFIG_AUDIO_EXCLUDE_TONE #ifndef CONFIG_AUDIO_EXCLUDE_TONE
static void wm8904_setbass(FAR struct wm8904_dev_s *priv, uint8_t bass); static void wm8904_setbass(FAR struct wm8904_dev_s *priv, uint8_t bass);
static void wm8904_settreble(FAR struct wm8904_dev_s *priv, uint8_t treble); static void wm8904_settreble(FAR struct wm8904_dev_s *priv,
uint8_t treble);
#endif #endif
static void wm8904_setdatawidth(FAR struct wm8904_dev_s *priv); static void wm8904_setdatawidth(FAR struct wm8904_dev_s *priv);
@ -225,9 +227,9 @@ static const struct audio_ops_s g_audioops =
#ifndef CONFIG_WM8904_CLKDEBUG #ifndef CONFIG_WM8904_CLKDEBUG
static static
#endif #endif
const uint8_t g_sysclk_scaleb1[WM8904_BCLK_MAXDIV+1] = const uint8_t g_sysclk_scaleb1[WM8904_BCLK_MAXDIV + 1] =
{ {
2, 3, 4, 6, 8, 10, 11, /* 1, 1.5, 2, 3, 4, 5, 5.5 */ 2, 3, 4, 6, 8, 10, 11, /* 1, 1.5, 2, 3, 4, 5, 5.5 */
12, 16, 20, 22, 24, 32, 40, /* 6, 8, 10, 11, 12, 16, 20 */ 12, 16, 20, 22, 24, 32, 40, /* 6, 8, 10, 11, 12, 16, 20 */
44, 48, 50, 60, 64, 88, 96 /* 22, 24, 25, 30, 32, 44, 48 */ 44, 48, 50, 60, 64, 88, 96 /* 22, 24, 25, 30, 32, 44, 48 */
}; };
@ -297,7 +299,8 @@ uint16_t wm8904_readreg(FAR struct wm8904_dev_s *priv, uint8_t regaddr)
if (retries < MAX_RETRIES) if (retries < MAX_RETRIES)
{ {
audwarn("WARNING: I2C_TRANSFER failed: %d ... Resetting\n", ret); audwarn("WARNING: I2C_TRANSFER failed: %d ... Resetting\n",
ret);
ret = I2C_RESET(priv->i2c); ret = I2C_RESET(priv->i2c);
if (ret < 0) if (ret < 0)
@ -331,13 +334,13 @@ uint16_t wm8904_readreg(FAR struct wm8904_dev_s *priv, uint8_t regaddr)
return 0; return 0;
} }
/************************************************************************************ /****************************************************************************
* Name: wm8904_writereg * Name: wm8904_writereg
* *
* Description: * Description:
* Write the specified 16-bit register to the WM8904 device. * Write the specified 16-bit register to the WM8904 device.
* *
************************************************************************************/ ****************************************************************************/
static void wm8904_writereg(FAR struct wm8904_dev_s *priv, uint8_t regaddr, static void wm8904_writereg(FAR struct wm8904_dev_s *priv, uint8_t regaddr,
uint16_t regval) uint16_t regval)
@ -405,28 +408,65 @@ static void wm8904_writereg(FAR struct wm8904_dev_s *priv, uint8_t regaddr,
} }
} }
/************************************************************************************ /****************************************************************************
* Name: wm8904_takesem * Name: wm8904_takesem
* *
* Description: * Description:
* Take a semaphore count, handling the nasty EINTR return if we are interrupted * Take a semaphore count, handling the nasty EINTR return if we are
* by a signal. * interrupted by a signal.
* *
************************************************************************************/ ****************************************************************************/
static void wm8904_takesem(sem_t *sem) static int wm8904_takesem(sem_t *sem)
{ {
nxsem_wait_uninterruptible(sem); return nxsem_wait_uninterruptible(sem);
} }
/************************************************************************************ /****************************************************************************
* Name: wm8904_forcetake
*
* Description:
* This is just another wrapper but this one continues even if the thread
* is canceled. This must be done in certain conditions where were must
* continue in order to clean-up resources.
*
****************************************************************************/
static int wm8904_forcetake(FAR sem_t *sem)
{
int result;
int ret = OK;
do
{
result = nxsem_wait_uninterruptible(sem);
/* The only expected error would -ECANCELED meaning that the
* parent thread has been canceled. We have to continue and
* terminate the poll in this case.
*/
DEBUGASSERT(result == OK || result == -ECANCELED);
if (ret == OK && result < 0)
{
/* Remember the first failure */
ret = result;
}
}
while (result < 0);
return ret;
}
/****************************************************************************
* Name: wm8904_scalevolume * Name: wm8904_scalevolume
* *
* Description: * Description:
* Set the right and left volume values in the WM8904 device based on the current * Set the right and left volume values in the WM8904 device based on the
* volume and balance settings. * current volume and balance settings.
* *
************************************************************************************/ ****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
static inline uint16_t wm8904_scalevolume(uint16_t volume, b16_t scale) static inline uint16_t wm8904_scalevolume(uint16_t volume, b16_t scale)
@ -435,14 +475,14 @@ static inline uint16_t wm8904_scalevolume(uint16_t volume, b16_t scale)
} }
#endif #endif
/************************************************************************************ /****************************************************************************
* Name: wm8904_setvolume * Name: wm8904_setvolume
* *
* Description: * Description:
* Set the right and left volume values in the WM8904 device based on the current * Set the right and left volume values in the WM8904 device based on the
* volume and balance settings. * current volume and balance settings.
* *
************************************************************************************/ ****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME #ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
static void wm8904_setvolume(FAR struct wm8904_dev_s *priv, uint16_t volume, static void wm8904_setvolume(FAR struct wm8904_dev_s *priv, uint16_t volume,
@ -514,7 +554,7 @@ static void wm8904_setvolume(FAR struct wm8904_dev_s *priv, uint16_t volume,
} }
#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */ #endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
/************************************************************************************ /****************************************************************************
* Name: wm8904_setbass * Name: wm8904_setbass
* *
* Description: * Description:
@ -522,7 +562,7 @@ static void wm8904_setvolume(FAR struct wm8904_dev_s *priv, uint16_t volume,
* *
* The level and range are in whole percentage levels (0-100). * The level and range are in whole percentage levels (0-100).
* *
************************************************************************************/ ****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_TONE #ifndef CONFIG_AUDIO_EXCLUDE_TONE
static void wm8904_setbass(FAR struct wm8904_dev_s *priv, uint8_t bass) static void wm8904_setbass(FAR struct wm8904_dev_s *priv, uint8_t bass)
@ -532,7 +572,7 @@ static void wm8904_setbass(FAR struct wm8904_dev_s *priv, uint8_t bass)
} }
#endif /* CONFIG_AUDIO_EXCLUDE_TONE */ #endif /* CONFIG_AUDIO_EXCLUDE_TONE */
/************************************************************************************ /****************************************************************************
* Name: wm8904_settreble * Name: wm8904_settreble
* *
* Description: * Description:
@ -540,7 +580,7 @@ static void wm8904_setbass(FAR struct wm8904_dev_s *priv, uint8_t bass)
* *
* The level and range are in whole percentage levels (0-100). * The level and range are in whole percentage levels (0-100).
* *
************************************************************************************/ ****************************************************************************/
#ifndef CONFIG_AUDIO_EXCLUDE_TONE #ifndef CONFIG_AUDIO_EXCLUDE_TONE
static void wm8904_settreble(FAR struct wm8904_dev_s *priv, uint8_t treble) static void wm8904_settreble(FAR struct wm8904_dev_s *priv, uint8_t treble)
@ -710,12 +750,12 @@ static void wm8904_setbitrate(FAR struct wm8904_dev_s *priv)
/* MCLK must be divided down so that fref <=13.5MHz */ /* MCLK must be divided down so that fref <=13.5MHz */
if (fref > 4*13500000) if (fref > 4 * 13500000)
{ {
fref >>= 3; fref >>= 3;
regval = (WM8904_FLL_CLK_REF_SRC_MCLK | WM8904_FLL_CLK_REF_DIV8); regval = (WM8904_FLL_CLK_REF_SRC_MCLK | WM8904_FLL_CLK_REF_DIV8);
} }
else if (fref > 2*13500000) else if (fref > 2 * 13500000)
{ {
fref >>= 2; fref >>= 2;
regval = (WM8904_FLL_CLK_REF_SRC_MCLK | WM8904_FLL_CLK_REF_DIV4); regval = (WM8904_FLL_CLK_REF_SRC_MCLK | WM8904_FLL_CLK_REF_DIV4);
@ -841,6 +881,7 @@ static void wm8904_setbitrate(FAR struct wm8904_dev_s *priv)
priv->bitrate = fout; priv->bitrate = fout;
/* Now, Configure the FLL */ /* Now, Configure the FLL */
/* FLL Control 1 /* FLL Control 1
* *
* FLL_FRACN_ENA=1 : Enables fractional mode * FLL_FRACN_ENA=1 : Enables fractional mode
@ -918,7 +959,7 @@ static void wm8904_setbitrate(FAR struct wm8904_dev_s *priv)
retries = 5; retries = 5;
do do
{ {
nxsig_usleep(5*5000); nxsig_usleep(5 * 5000);
} }
while (priv->locked == false && --retries > 0); while (priv->locked == false && --retries > 0);
@ -941,10 +982,11 @@ static void wm8904_setbitrate(FAR struct wm8904_dev_s *priv)
retries = 5; retries = 5;
do do
{ {
nxsig_usleep(5*5000); nxsig_usleep(5 * 5000);
} }
while ((wm8904_readreg(priv, WM8904_INT_STATUS) & WM8904_FLL_LOCK_INT) != 0 || while ((wm8904_readreg(priv, WM8904_INT_STATUS) &
--retries > 0); WM8904_FLL_LOCK_INT) != 0 ||
--retries > 0);
/* Clear all pending status bits by writing 1's into the interrupt status /* Clear all pending status bits by writing 1's into the interrupt status
* register. * register.
@ -996,18 +1038,21 @@ static int wm8904_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
switch (caps->ac_subtype) switch (caps->ac_subtype)
{ {
case AUDIO_TYPE_QUERY: case AUDIO_TYPE_QUERY:
/* We don't decode any formats! Only something above us in /* We don't decode any formats! Only something above us in
* the audio stream can perform decoding on our behalf. * the audio stream can perform decoding on our behalf.
*/ */
/* The types of audio units we implement */ /* The types of audio units we implement */
caps->ac_controls.b[0] = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE | caps->ac_controls.b[0] =
AUDIO_TYPE_PROCESSING; AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE |
AUDIO_TYPE_PROCESSING;
break; break;
case AUDIO_FMT_MIDI: case AUDIO_FMT_MIDI:
/* We only support Format 0 */ /* We only support Format 0 */
caps->ac_controls.b[0] = AUDIO_SUBFMT_END; caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
@ -1032,10 +1077,11 @@ static int wm8904_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
/* Report the Sample rates we support */ /* Report the Sample rates we support */
caps->ac_controls.b[0] = AUDIO_SAMP_RATE_8K | AUDIO_SAMP_RATE_11K | caps->ac_controls.b[0] =
AUDIO_SAMP_RATE_16K | AUDIO_SAMP_RATE_22K | AUDIO_SAMP_RATE_8K | AUDIO_SAMP_RATE_11K |
AUDIO_SAMP_RATE_32K | AUDIO_SAMP_RATE_44K | AUDIO_SAMP_RATE_16K | AUDIO_SAMP_RATE_22K |
AUDIO_SAMP_RATE_48K; AUDIO_SAMP_RATE_32K | AUDIO_SAMP_RATE_44K |
AUDIO_SAMP_RATE_48K;
break; break;
case AUDIO_FMT_MP3: case AUDIO_FMT_MP3:
@ -1059,13 +1105,14 @@ static int wm8904_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
{ {
/* Fill in the ac_controls section with the Feature Units we have */ /* 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[0] = AUDIO_FU_VOLUME | AUDIO_FU_BASS |
AUDIO_FU_TREBLE;
caps->ac_controls.b[1] = AUDIO_FU_BALANCE >> 8; caps->ac_controls.b[1] = AUDIO_FU_BALANCE >> 8;
} }
else else
{ {
/* TODO: Do we need to provide specific info for the Feature Units, /* TODO: Do we need to provide specific info for the Feature
* such as volume setting ranges, etc.? * Units, such as volume setting ranges, etc.?
*/ */
} }
@ -1088,7 +1135,8 @@ static int wm8904_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
/* Provide capabilities of our Stereo Extender */ /* Provide capabilities of our Stereo Extender */
caps->ac_controls.b[0] = AUDIO_STEXT_ENABLE | AUDIO_STEXT_WIDTH; caps->ac_controls.b[0] =
AUDIO_STEXT_ENABLE | AUDIO_STEXT_WIDTH;
break; break;
default: default:
@ -1342,6 +1390,7 @@ static void wm8904_senddone(FAR struct i2s_dev_s *i2s,
priv->inflight--; priv->inflight--;
/* Save the result of the transfer */ /* Save the result of the transfer */
/* REVISIT: This can be overwritten */ /* REVISIT: This can be overwritten */
priv->result = result; priv->result = result;
@ -1444,7 +1493,7 @@ static int wm8904_sendbuffer(FAR struct wm8904_dev_s *priv)
irqstate_t flags; irqstate_t flags;
uint32_t timeout; uint32_t timeout;
int shift; int shift;
int ret = OK; int ret;
/* Loop while there are audio buffers to be sent and we have few than /* Loop while there are audio buffers to be sent and we have few than
* CONFIG_WM8904_INFLIGHT then "in-flight" * CONFIG_WM8904_INFLIGHT then "in-flight"
@ -1458,7 +1507,12 @@ static int wm8904_sendbuffer(FAR struct wm8904_dev_s *priv)
* only while accessing 'inflight'. * only while accessing 'inflight'.
*/ */
wm8904_takesem(&priv->pendsem); ret = wm8904_takesem(&priv->pendsem);
if (ret < 0)
{
return ret;
}
while (priv->inflight < CONFIG_WM8904_INFLIGHT && while (priv->inflight < CONFIG_WM8904_INFLIGHT &&
dq_peek(&priv->pendq) != NULL && !priv->paused) dq_peek(&priv->pendq) != NULL && !priv->paused)
{ {
@ -1540,6 +1594,7 @@ static int wm8904_start(FAR struct audio_lowerhalf_s *dev)
audinfo("Entry\n"); audinfo("Entry\n");
/* Exit reduced power modes of operation */ /* Exit reduced power modes of operation */
/* REVISIT */ /* REVISIT */
/* Create a message queue for the worker thread */ /* Create a message queue for the worker thread */
@ -1623,6 +1678,7 @@ static int wm8904_stop(FAR struct audio_lowerhalf_s *dev)
priv->threadid = 0; priv->threadid = 0;
/* Enter into a reduced power usage mode */ /* Enter into a reduced power usage mode */
/* REVISIT: */ /* REVISIT: */
return OK; return OK;
@ -1667,7 +1723,8 @@ static int wm8904_pause(FAR struct audio_lowerhalf_s *dev)
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
#ifdef CONFIG_AUDIO_MULTI_SESSION #ifdef CONFIG_AUDIO_MULTI_SESSION
static int wm8904_resume(FAR struct audio_lowerhalf_s *dev, FAR void *session) static int wm8904_resume(FAR struct audio_lowerhalf_s *dev,
FAR void *session)
#else #else
static int wm8904_resume(FAR struct audio_lowerhalf_s *dev) static int wm8904_resume(FAR struct audio_lowerhalf_s *dev)
#endif #endif
@ -1714,14 +1771,20 @@ static int wm8904_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
/* Add the new buffer to the tail of pending audio buffers */ /* Add the new buffer to the tail of pending audio buffers */
wm8904_takesem(&priv->pendsem); ret = wm8904_takesem(&priv->pendsem);
if (ret < 0)
{
return ret;
}
apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED; apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED;
dq_addlast(&apb->dq_entry, &priv->pendq); dq_addlast(&apb->dq_entry, &priv->pendq);
wm8904_givesem(&priv->pendsem); wm8904_givesem(&priv->pendsem);
/* Send a message to the worker thread indicating that a new buffer has been /* Send a message to the worker thread indicating that a new buffer has
* enqueued. If mq is NULL, then the playing has not yet started. In that * been enqueued. If mq is NULL, then the playing has not yet started.
* case we are just "priming the pump" and we don't need to send any message. * In that case we are just "priming the pump" and we don't need to send
* any message.
*/ */
ret = OK; ret = OK;
@ -1824,11 +1887,16 @@ static int wm8904_reserve(FAR struct audio_lowerhalf_s *dev)
#endif #endif
{ {
FAR struct wm8904_dev_s *priv = (FAR struct wm8904_dev_s *) dev; FAR struct wm8904_dev_s *priv = (FAR struct wm8904_dev_s *) dev;
int ret = OK; int ret;
/* Borrow the APBQ semaphore for thread sync */ /* Borrow the APBQ semaphore for thread sync */
wm8904_takesem(&priv->pendsem); ret = wm8904_takesem(&priv->pendsem);
if (ret < 0)
{
return ret;
}
if (priv->reserved) if (priv->reserved)
{ {
ret = -EBUSY; ret = -EBUSY;
@ -1869,7 +1937,8 @@ static int wm8904_release(FAR struct audio_lowerhalf_s *dev)
#endif #endif
{ {
FAR struct wm8904_dev_s *priv = (FAR struct wm8904_dev_s *)dev; FAR struct wm8904_dev_s *priv = (FAR struct wm8904_dev_s *)dev;
void *value; FAR void *value;
int ret;
/* Join any old worker thread we had created to prevent a memory leak */ /* Join any old worker thread we had created to prevent a memory leak */
@ -1881,14 +1950,14 @@ static int wm8904_release(FAR struct audio_lowerhalf_s *dev)
/* Borrow the APBQ semaphore for thread sync */ /* Borrow the APBQ semaphore for thread sync */
wm8904_takesem(&priv->pendsem); ret = wm8904_forcetake(&priv->pendsem);
/* Really we should free any queued buffers here */ /* Really we should free any queued buffers here */
priv->reserved = false; priv->reserved = false;
wm8904_givesem(&priv->pendsem); wm8904_givesem(&priv->pendsem);
return OK; return ret;
} }
/**************************************************************************** /****************************************************************************
@ -2069,6 +2138,7 @@ static void *wm8904_workerthread(pthread_addr_t pvarg)
#ifndef CONFIG_AUDIO_EXCLUDE_STOP #ifndef CONFIG_AUDIO_EXCLUDE_STOP
case AUDIO_MSG_STOP: case AUDIO_MSG_STOP:
/* Indicate that we are terminating */ /* Indicate that we are terminating */
audinfo("AUDIO_MSG_STOP: Terminating\n"); audinfo("AUDIO_MSG_STOP: Terminating\n");
@ -2103,7 +2173,7 @@ static void *wm8904_workerthread(pthread_addr_t pvarg)
/* Return any pending buffers in our pending queue */ /* Return any pending buffers in our pending queue */
wm8904_takesem(&priv->pendsem); wm8904_forcetake(&priv->pendsem);
while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL) while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL)
{ {
/* Release our reference to the buffer */ /* Release our reference to the buffer */
@ -2174,6 +2244,7 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv)
wm8904_writereg(priv, WM8904_VMID_CTRL, regval); wm8904_writereg(priv, WM8904_VMID_CTRL, regval);
/* Mic Bias Control 0 */ /* Mic Bias Control 0 */
/* MICDET_ENA=1, MICBIAS_ENA=1 */ /* MICDET_ENA=1, MICBIAS_ENA=1 */
regval = WM8904_MICDET_ENA | WM8904_MICBIAS_ENA; regval = WM8904_MICDET_ENA | WM8904_MICBIAS_ENA;
@ -2194,15 +2265,17 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv)
wm8904_writereg(priv, WM8904_PM2, regval); wm8904_writereg(priv, WM8904_PM2, regval);
/* Power Management 6 */ /* Power Management 6 */
/* DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1 */ /* DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1 */
regval = WM8904_DACL_ENA | WM8904_DACR_ENA | WM8904_ADCL_ENA | WM8904_ADCR_ENA; regval = WM8904_DACL_ENA | WM8904_DACR_ENA | WM8904_ADCL_ENA |
WM8904_ADCR_ENA;
wm8904_writereg(priv, WM8904_PM6, regval); wm8904_writereg(priv, WM8904_PM6, regval);
/* Clock Rates 0. /* Clock Rates 0.
* *
* This value sets TOCLK_RATE_DIV16=0, TOCLK_RATE_X4=0, and MCLK_DIV=0 while * This value sets TOCLK_RATE_DIV16=0, TOCLK_RATE_X4=0, and MCLK_DIV=0
* preserving the state of some undocumented bits (see wm8904.h). * while preserving the state of some undocumented bits (see wm8904.h).
* *
* MCLK_DIV=0 : MCLK is is not divided by 2. * MCLK_DIV=0 : MCLK is is not divided by 2.
*/ */
@ -2244,13 +2317,14 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv)
/* Audio Interface 1. /* Audio Interface 1.
* *
* This value sets AIFADC_TDM=0, AIFADC_TDM_CHAN=0, BCLK_DIR=1 while preserving * This value sets AIFADC_TDM=0, AIFADC_TDM_CHAN=0, BCLK_DIR=1 while
* the state of some undocumented bits (see wm8904.h). * preserving the state of some undocumented bits (see wm8904.h).
* *
* Digital audio interface format : I2S * Digital audio interface format : I2S
* Digital audio interface word length : 24 * Digital audio interface word length : 24
* AIF_LRCLK_INV=0 : LRCLK not inverted * AIF_LRCLK_INV=0 : LRCLK not inverted
* BCLK_DIR=1 : BCLK is an output (will clock I2S). * BCLK_DIR=1 : BCLK is an output (will clock
* I2S).
* AIF_BCLK_INV=0 : BCLK not inverted * AIF_BCLK_INV=0 : BCLK not inverted
* AIF_TRIS=0 : Outputs not tri-stated * AIF_TRIS=0 : Outputs not tri-stated
* AIFADC_TDM_CHAN=0 : ADCDAT outputs data on slot 0 * AIFADC_TDM_CHAN=0 : ADCDAT outputs data on slot 0
@ -2260,7 +2334,8 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv)
* Bit 14: : Undocumented * Bit 14: : Undocumented
*/ */
regval = WM8904_AIF_FMT_I2S | WM8904_AIF_WL_24BITS | WM8904_BCLK_DIR | 0x4000; regval = WM8904_AIF_FMT_I2S | WM8904_AIF_WL_24BITS | WM8904_BCLK_DIR |
0x4000;
wm8904_writereg(priv, WM8904_AIF1, regval); wm8904_writereg(priv, WM8904_AIF1, regval);
/* Audio Interface 2. /* Audio Interface 2.
@ -2271,10 +2346,10 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv)
/* Audio Interface 3 /* Audio Interface 3
* *
* Set LRCLK as an output with rate = BCLK / (2*WM8904_FRAMELENn). This is * Set LRCLK as an output with rate = BCLK / (2*WM8904_FRAMELENn). This
* a value that varies with bits per sample, n=8 or 16. Since I2S will send * is a value that varies with bits per sample, n=8 or 16. Since I2S will
* a word on each edge of LRCLK (after a delay), this essentially means that * send a word on each edge of LRCLK (after a delay), this essentially
* each audio frame is WM8904_FRAMELENn bits in length. * means that each audio frame is WM8904_FRAMELENn bits in length.
*/ */
regval = WM8904_LRCLK_DIR | WM8904_LRCLK_RATE(2*WM8904_FRAMELEN16); regval = WM8904_LRCLK_DIR | WM8904_LRCLK_RATE(2*WM8904_FRAMELEN16);
@ -2285,6 +2360,7 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv)
wm8904_writereg(priv, WM8904_DAC_DIGI1, 0); wm8904_writereg(priv, WM8904_DAC_DIGI1, 0);
/* Analogue Left Input 0 */ /* Analogue Left Input 0 */
/* Analogue Right Input 0 */ /* Analogue Right Input 0 */
regval = WM8904_IN_VOL(5); regval = WM8904_IN_VOL(5);
@ -2297,6 +2373,7 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv)
wm8904_writereg(priv, WM8904_ANA_RIGHT_IN1, 0); wm8904_writereg(priv, WM8904_ANA_RIGHT_IN1, 0);
/* Analogue OUT1 Left */ /* Analogue OUT1 Left */
/* Analogue OUT1 Right */ /* Analogue OUT1 Right */
wm8904_setvolume(priv, CONFIG_WM8904_INITVOLUME, true); wm8904_setvolume(priv, CONFIG_WM8904_INITVOLUME, true);
@ -2308,8 +2385,9 @@ static void wm8904_audio_output(FAR struct wm8904_dev_s *priv)
/* Analogue HP 0 */ /* Analogue HP 0 */
regval = WM8904_HPL_RMV_SHORT | WM8904_HPL_ENA_OUTP | WM8904_HPL_ENA_DLY | WM8904_HPL_ENA | regval = WM8904_HPL_RMV_SHORT | WM8904_HPL_ENA_OUTP | WM8904_HPL_ENA_DLY |
WM8904_HPR_RMV_SHORT | WM8904_HPR_ENA_OUTP | WM8904_HPR_ENA_DLY | WM8904_HPR_ENA; WM8904_HPL_ENA | WM8904_HPR_RMV_SHORT | WM8904_HPR_ENA_OUTP |
WM8904_HPR_ENA_DLY | WM8904_HPR_ENA;
wm8904_writereg(priv, WM8904_ANA_HP0, regval); wm8904_writereg(priv, WM8904_ANA_HP0, regval);
/* Charge Pump 0 */ /* Charge Pump 0 */

View File

@ -1,51 +1,40 @@
/************************************************************************************** /****************************************************************************
* drivers/lcd/ft80x.c * drivers/lcd/ft80x.c
* *
* Copyright (C) 2018 Gregory Nutt. All rights reserved. * Licensed to the Apache Software Foundation (ASF) under one or more
* Author: Gregory Nutt <gnutt@nuttx.org> * 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
* *
* References: * http://www.apache.org/licenses/LICENSE-2.0
* - Document No.: FT_000792, "FT800 Embedded Video Engine", Datasheet Version 1.1,
* Clearance No.: FTDI# 334, Future Technology Devices International Ltd.
* - Document No.: FT_000986, "FT801 Embedded Video Engine Datasheet", Version 1.0,
* Clearance No.: FTDI#376, Future Technology Devices International Ltd.
* - Application Note AN_240AN_240, "FT800 From the Ground Up", Version 1.1,
* Issue Date: 2014-06-09, Future Technology Devices International Ltd.
* - "FT800 Series Programmer Guide Guide", Version 2.1, Issue Date: 2016-09-19,
* Future Technology Devices International Ltd.
* *
* Redistribution and use in source and binary forms, with or without * Unless required by applicable law or agreed to in writing, software
* modification, are permitted provided that the following conditions * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* are met: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
* *
* 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.
*
**************************************************************************************/
/************************************************************************************** /* References:
* - Document No.: FT_000792, "FT800 Embedded Video Engine", Datasheet
* Version 1.1, Clearance No.: FTDI# 334, Future Technology Devices
* International Ltd.
* - Document No.: FT_000986, "FT801 Embedded Video Engine Datasheet",
* Version 1.0, Clearance No.: FTDI#376, Future Technology Devices
* International Ltd.
* - Application Note AN_240AN_240, "FT800 From the Ground Up", Version
* 1.1, Issue Date: 2014-06-09, Future Technology Devices International
* Ltd.
* - "FT800 Series Programmer Guide Guide", Version 2.1, Issue Date:
* 2016-09-19, Future Technology Devices International Ltd.
*/
/****************************************************************************
* Included Files * Included Files
**************************************************************************************/ ****************************************************************************/
#include <nuttx/config.h> #include <nuttx/config.h>
@ -154,6 +143,35 @@ static const struct file_operations g_ft80x_fops =
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: ft80x_forcetake
*
* Description:
* This is a wrapper around but nxsem_wait_uninterruptible(). The wrapper
* continues to wait even if the thread is canceled. This must be done in
* certain conditions where were must continue in order to clean-up
* resources.
*
****************************************************************************/
static void ft80x_forcetake(FAR sem_t *sem)
{
int ret;
do
{
ret = nxsem_wait_uninterruptible(sem);
/* The only expected error would -ECANCELED meaning that the
* parent thread has been canceled. We have to continue and
* terminate the poll in this case.
*/
DEBUGASSERT(ret == OK || ret == -ECANCELED);
}
while (ret < 0);
}
/**************************************************************************** /****************************************************************************
* Name: ft80x_fade * Name: ft80x_fade
* *
@ -295,7 +313,7 @@ static void ft80x_interrupt_work(FAR void *arg)
/* Get exclusive access to the device structures */ /* Get exclusive access to the device structures */
nxsem_wait_uninterruptible(&priv->exclsem); ft80x_forcetake(&priv->exclsem);
/* Get the set of pending interrupts. Note that simply reading this /* Get the set of pending interrupts. Note that simply reading this
* register is sufficient to clear all pending interrupts. * register is sufficient to clear all pending interrupts.
@ -309,74 +327,74 @@ static void ft80x_interrupt_work(FAR void *arg)
* implementation. * implementation.
*/ */
if ((intflags & FT80X_INT_SWAP) != 0) if ((intflags & FT80X_INT_SWAP) != 0)
{ {
/* Display swap occurred */ /* Display swap occurred */
lcdinfo("Display swap occurred\n"); lcdinfo("Display swap occurred\n");
ft80x_notify(priv, FT80X_NOTIFY_SWAP, 0); ft80x_notify(priv, FT80X_NOTIFY_SWAP, 0);
} }
if ((intflags & FT80X_INT_TOUCH) != 0) if ((intflags & FT80X_INT_TOUCH) != 0)
{ {
/* Touch-screen touch detected */ /* Touch-screen touch detected */
lcdinfo("Touch-screen touch detected\n"); lcdinfo("Touch-screen touch detected\n");
ft80x_notify(priv, FT80X_NOTIFY_TOUCH, 0); ft80x_notify(priv, FT80X_NOTIFY_TOUCH, 0);
} }
if ((intflags & FT80X_INT_TAG) != 0) if ((intflags & FT80X_INT_TAG) != 0)
{ {
/* Touch-screen tag value change */ /* Touch-screen tag value change */
lcdinfo("Touch-screen tag value change\n"); lcdinfo("Touch-screen tag value change\n");
#ifdef CONFIG_LCD_FT800 #ifdef CONFIG_LCD_FT800
regval = ft80x_read_word(priv, FT80X_REG_TOUCH_TAG); regval = ft80x_read_word(priv, FT80X_REG_TOUCH_TAG);
#else #else
regval = ft80x_read_word(priv, FT80X_REG_CTOUCH_TAG); regval = ft80x_read_word(priv, FT80X_REG_CTOUCH_TAG);
#endif #endif
ft80x_notify(priv, FT80X_NOTIFY_TAG, (int)(regval & TOUCH_TAG_MASK)); ft80x_notify(priv, FT80X_NOTIFY_TAG, (int)(regval & TOUCH_TAG_MASK));
} }
if ((intflags & FT80X_INT_SOUND) != 0) if ((intflags & FT80X_INT_SOUND) != 0)
{ {
/* Sound effect ended */ /* Sound effect ended */
lcdinfo(" Sound effect ended\n"); lcdinfo(" Sound effect ended\n");
ft80x_notify(priv, FT80X_NOTIFY_SOUND, 0); ft80x_notify(priv, FT80X_NOTIFY_SOUND, 0);
} }
if ((intflags & FT80X_INT_PLAYBACK) != 0) if ((intflags & FT80X_INT_PLAYBACK) != 0)
{ {
/* Audio playback ended */ /* Audio playback ended */
lcdinfo("Audio playback ended\n"); lcdinfo("Audio playback ended\n");
ft80x_notify(priv, FT80X_NOTIFY_PLAYBACK, 0); ft80x_notify(priv, FT80X_NOTIFY_PLAYBACK, 0);
} }
if ((intflags & FT80X_INT_CMDEMPTY) != 0) if ((intflags & FT80X_INT_CMDEMPTY) != 0)
{ {
/* Command FIFO empty */ /* Command FIFO empty */
lcdinfo("Command FIFO empty\n"); lcdinfo("Command FIFO empty\n");
ft80x_notify(priv, FT80X_NOTIFY_CMDEMPTY, 0); ft80x_notify(priv, FT80X_NOTIFY_CMDEMPTY, 0);
} }
if ((intflags & FT80X_INT_CMDFLAG) != 0) if ((intflags & FT80X_INT_CMDFLAG) != 0)
{ {
/* Command FIFO flag */ /* Command FIFO flag */
lcdinfo("Command FIFO flag\n"); lcdinfo("Command FIFO flag\n");
ft80x_notify(priv, FT80X_NOTIFY_CMDFLAG, 0); ft80x_notify(priv, FT80X_NOTIFY_CMDFLAG, 0);
} }
if ((intflags & FT80X_INT_CONVCOMPLETE) != 0) if ((intflags & FT80X_INT_CONVCOMPLETE) != 0)
{ {
/* Touch-screen conversions completed */ /* Touch-screen conversions completed */
lcdinfo(" Touch-screen conversions completed\n"); lcdinfo(" Touch-screen conversions completed\n");
ft80x_notify(priv, FT80X_NOTIFY_CONVCOMPLETE, 0); ft80x_notify(priv, FT80X_NOTIFY_CONVCOMPLETE, 0);
} }
/* Re-enable interrupts */ /* Re-enable interrupts */
@ -598,7 +616,7 @@ static ssize_t ft80x_write(FAR struct file *filep, FAR const char *buffer,
if (buffer == NULL || ((uintptr_t)buffer & 3) != 0 || if (buffer == NULL || ((uintptr_t)buffer & 3) != 0 ||
len == 0 || (len & 3) != 0 || (len + filep->f_pos) > FT80X_RAM_DL_SIZE) len == 0 || (len & 3) != 0 || (len + filep->f_pos) > FT80X_RAM_DL_SIZE)
{ {
return -EINVAL; return -EINVAL;
} }
/* Get exclusive access to the device structures */ /* Get exclusive access to the device structures */
@ -659,12 +677,14 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
switch (cmd) switch (cmd)
{ {
/* FT80X_IOC_CREATEDL: /* FT80X_IOC_CREATEDL:
* Description: Write a display list to the FT80x display list memory * Description: Write a display list to the FT80x display list
* Description: Write a display list to the FT80x display list memory * memory
* starting at offset zero. This may or may not be the * Description: Write a display list to the FT80x display list
* entire display list. Display lists may be created * memory starting at offset zero. This may or may
* incrementally, starting with FT80X_IOC_CREATEDL and * not be the entire display list. Display lists may
* finishing the display list using FT80XIO_APPENDDL * be created incrementally, starting with
* FT80X_IOC_CREATEDL and finishing the display list
* using FT80XIO_APPENDDL
* Argument: A reference to a display list structure instance. * Argument: A reference to a display list structure instance.
* See struct ft80x_displaylist_s. * See struct ft80x_displaylist_s.
* Returns: None * Returns: None
@ -682,12 +702,13 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_APPENDDL: /* FT80X_IOC_APPENDDL:
* Description: Write additional display list entries to the FT80x * Description: Write additional display list entries to the FT80x
* display list memory at the current display list offset. * display list memory at the current display list
* This IOCTL command permits display lists to be completed * offset. This IOCTL command permits display lists
* incrementally, starting with FT80X_IOC_CREATEDL and * to be completed incrementally, starting with
* finishing the display list using FT80XIO_APPENDDL. * FT80X_IOC_CREATEDL and finishing the display list
* Argument: A reference to a display list structure instance. See * using FT80XIO_APPENDDL.
* struct ft80x_displaylist_s. * Argument: A reference to a display list structure instance.
* See struct ft80x_displaylist_s.
* Returns: None * Returns: None
*/ */
@ -778,7 +799,7 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_PUTRAMCMD /* FT80X_IOC_PUTRAMCMD
* Description: Write 32-bit aligned data to FT80x FIFO (RAM_CMD) * Description: Write 32-bit aligned data to FT80x FIFO (RAM_CMD)
* Argument: A reference to an instance of struct ft80x_relmem_s below. * Argument: A reference to an instance of struct ft80x_relmem_s.
* Returns: None. * Returns: None.
*/ */
@ -787,8 +808,7 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
FAR struct ft80x_relmem_s *ramcmd = FAR struct ft80x_relmem_s *ramcmd =
(FAR struct ft80x_relmem_s *)((uintptr_t)arg); (FAR struct ft80x_relmem_s *)((uintptr_t)arg);
if (ramcmd == NULL || ((uintptr_t)ramcmd->offset & 3) != 0 /* || if (ramcmd == NULL || ((uintptr_t)ramcmd->offset & 3) != 0)
ramcmd->offset >= FT80X_CMDFIFO_SIZE */ )
{ {
ret = -EINVAL; ret = -EINVAL;
} }
@ -803,7 +823,8 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_GETREG8: /* FT80X_IOC_GETREG8:
* Description: Read an 8-bit register value from the FT80x. * Description: Read an 8-bit register value from the FT80x.
* Argument: A reference to an instance of struct ft80x_register_s. * Argument: A reference to an instance of struct
* ft80x_register_s.
* Returns: The 8-bit value read from the register. * Returns: The 8-bit value read from the register.
*/ */
@ -826,7 +847,8 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_GETREG16: /* FT80X_IOC_GETREG16:
* Description: Read a 16-bit register value from the FT80x. * Description: Read a 16-bit register value from the FT80x.
* Argument: A reference to an instance of struct ft80x_register_s. * Argument: A reference to an instance of struct
* ft80x_register_s.
* Returns: The 16-bit value read from the register. * Returns: The 16-bit value read from the register.
*/ */
@ -849,7 +871,8 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_GETREG32: /* FT80X_IOC_GETREG32:
* Description: Read a 32-bit register value from the FT80x. * Description: Read a 32-bit register value from the FT80x.
* Argument: A reference to an instance of struct ft80x_register_s. * Argument: A reference to an instance of struct
* ft80x_register_s.
* Returns: The 32-bit value read from the register. * Returns: The 32-bit value read from the register.
*/ */
@ -872,8 +895,10 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_GETREGS: /* FT80X_IOC_GETREGS:
* Description: Read multiple 32-bit register values from the FT80x. * Description: Read multiple 32-bit register values from the FT80x.
* Argument: A reference to an instance of struct ft80x_registers_s. * Argument: A reference to an instance of struct
* Returns: The 32-bit values read from the consecutive registers . * ft80x_registers_s.
* Returns: The 32-bit values read from the consecutive
* registers .
*/ */
case FT80X_IOC_GETREGS: case FT80X_IOC_GETREGS:
@ -896,7 +921,8 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_PUTREG8: /* FT80X_IOC_PUTREG8:
* Description: Write an 8-bit register value to the FT80x. * Description: Write an 8-bit register value to the FT80x.
* Argument: A reference to an instance of struct ft80x_register_s. * Argument: A reference to an instance of struct
* ft80x_register_s.
* Returns: None. * Returns: None.
*/ */
@ -919,7 +945,8 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_PUTREG16: /* FT80X_IOC_PUTREG16:
* Description: Write a 16-bit register value to the FT80x. * Description: Write a 16-bit register value to the FT80x.
* Argument: A reference to an instance of struct ft80x_register_s. * Argument: A reference to an instance of struct
* ft80x_register_s.
* Returns: None. * Returns: None.
*/ */
@ -942,7 +969,8 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_PUTREG32: /* FT80X_IOC_PUTREG32:
* Description: Write a 32-bit register value to the FT80x. * Description: Write a 32-bit register value to the FT80x.
* Argument: A reference to an instance of struct ft80x_register_s. * Argument: A reference to an instance of struct
* ft80x_register_s.
* Returns: None. * Returns: None.
*/ */
@ -965,7 +993,8 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
/* FT80X_IOC_PUTREGS: /* FT80X_IOC_PUTREGS:
* Description: Write multiple 32-bit register values to the FT80x. * Description: Write multiple 32-bit register values to the FT80x.
* Argument: A reference to an instance of struct ft80x_registers_s. * Argument: A reference to an instance of struct
* ft80x_registers_s.
* Returns: None. * Returns: None.
*/ */
@ -1091,7 +1120,8 @@ static int ft80x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
#if defined(CONFIG_LCD_FT80X_AUDIO_MCUSHUTDOWN) #if defined(CONFIG_LCD_FT80X_AUDIO_MCUSHUTDOWN)
/* Amplifier is controlled by an MCU GPIO pin */ /* Amplifier is controlled by an MCU GPIO pin */
DEBUGASSERT(priv->lower->attach != NULL && priv->lower->audio != NULL); DEBUGASSERT(priv->lower->attach != NULL &&
priv->lower->audio != NULL);
DEBUGASSERT(arg == 0 || arg == 1); DEBUGASSERT(arg == 0 || arg == 1);
priv->lower->audio(priv->lower, (arg != 0)); priv->lower->audio(priv->lower, (arg != 0));
@ -1207,8 +1237,8 @@ static int ft80x_initialize(FAR struct ft80x_dev_s *priv)
* - FT80X_REG_VSYNC0 * - FT80X_REG_VSYNC0
* - FT80X_REG_VSYNC1 * - FT80X_REG_VSYNC1
* *
* And the FT80X_REG_CSPREAD register changes color clock timing to reduce system * And the FT80X_REG_CSPREAD register changes color clock timing to reduce
* noise. * system noise.
* *
* GPIO bit 7 is used for the display enable pin of the LCD module. By * GPIO bit 7 is used for the display enable pin of the LCD module. By
* setting the direction of the GPIO bit to out direction, the display can * setting the direction of the GPIO bit to out direction, the display can
@ -1221,7 +1251,8 @@ static int ft80x_initialize(FAR struct ft80x_dev_s *priv)
* *
* 1. Drive the PD_N pin high * 1. Drive the PD_N pin high
* 2. Wait for at least 20ms * 2. Wait for at least 20ms
* 3. Execute "Initialization Sequence during the Boot up" from steps 1 to 9 * 3. Execute "Initialization Sequence during the Boot up" from steps 1
* to 9
* *
* Initialization Sequence from Sleep Mode: * Initialization Sequence from Sleep Mode:
* *
@ -1235,9 +1266,9 @@ static int ft80x_initialize(FAR struct ft80x_dev_s *priv)
* Mode" except waiting for at least 20ms in step 2. * Mode" except waiting for at least 20ms in step 2.
*/ */
DEBUGASSERT(priv->lower != NULL && priv->lower->pwrdown != NULL); DEBUGASSERT(priv->lower != NULL && priv->lower->pwrdown != NULL);
priv->lower->pwrdown(priv->lower, false); priv->lower->pwrdown(priv->lower, false);
up_mdelay(20); up_mdelay(20);
/* Initialization Sequence during the boot up: /* Initialization Sequence during the boot up:
* *
@ -1320,8 +1351,8 @@ static int ft80x_initialize(FAR struct ft80x_dev_s *priv)
* checked, the next task is to configure the LCD display parameters for * checked, the next task is to configure the LCD display parameters for
* the chosen display with the values determined in Section 2.3.3 above. * the chosen display with the values determined in Section 2.3.3 above.
* *
* a. Set FT80X_REG_PCLK to zero - This disables the pixel clock output while * a. Set FT80X_REG_PCLK to zero - This disables the pixel clock output
* the LCD and other system parameters are configured * while the LCD and other system parameters are configured
* b. Set the following registers with values for the chosen display. * b. Set the following registers with values for the chosen display.
* Typical WQVGA and QVGA values are shown: * Typical WQVGA and QVGA values are shown:
* *
@ -1388,8 +1419,8 @@ static int ft80x_initialize(FAR struct ft80x_dev_s *priv)
/* 5. Write first display list */ /* 5. Write first display list */
ft80x_write_word(priv, FT80X_RAM_DL + 0, FT80X_CLEAR_COLOR_RGB(0,0,0)); ft80x_write_word(priv, FT80X_RAM_DL + 0, FT80X_CLEAR_COLOR_RGB(0, 0, 0));
ft80x_write_word(priv, FT80X_RAM_DL + 4, FT80X_CLEAR(1,1,1)); ft80x_write_word(priv, FT80X_RAM_DL + 4, FT80X_CLEAR(1, 1, 1));
ft80x_write_word(priv, FT80X_RAM_DL + 8, FT80X_DISPLAY()); ft80x_write_word(priv, FT80X_RAM_DL + 8, FT80X_DISPLAY());
/* 6. Write FT80X_REG_DLSWAP, FT800 swaps display list immediately */ /* 6. Write FT80X_REG_DLSWAP, FT800 swaps display list immediately */

View File

@ -101,7 +101,7 @@ struct phy_notify_s
* Private Function Prototypes * Private Function Prototypes
****************************************************************************/ ****************************************************************************/
static void phy_semtake(void); static int phy_semtake(void);
static FAR struct phy_notify_s *phy_find_unassigned(void); static FAR struct phy_notify_s *phy_find_unassigned(void);
static FAR struct phy_notify_s *phy_find_assigned(FAR const char *intf, static FAR struct phy_notify_s *phy_find_assigned(FAR const char *intf,
pid_t pid); pid_t pid);
@ -128,9 +128,9 @@ static struct phy_notify_s
* Name: phy_semtake * Name: phy_semtake
****************************************************************************/ ****************************************************************************/
static void phy_semtake(void) static int phy_semtake(void)
{ {
nxsem_wait_uninterruptible(&g_notify_clients_sem); return nxsem_wait_uninterruptible(&g_notify_clients_sem);
} }
#define phy_semgive() nxsem_post(&g_notify_clients_sem); #define phy_semgive() nxsem_post(&g_notify_clients_sem);
@ -142,9 +142,16 @@ static void phy_semtake(void)
static FAR struct phy_notify_s *phy_find_unassigned(void) static FAR struct phy_notify_s *phy_find_unassigned(void)
{ {
FAR struct phy_notify_s *client; FAR struct phy_notify_s *client;
int ret;
int i; int i;
phy_semtake(); ret = phy_semtake();
if (ret < 0)
{
phyerr("ERROR: phy_semtake failed: %d\n", ret);
return NULL;
}
for (i = 0; i < CONFIG_PHY_NOTIFICATION_NCLIENTS; i++) for (i = 0; i < CONFIG_PHY_NOTIFICATION_NCLIENTS; i++)
{ {
client = &g_notify_clients[i]; client = &g_notify_clients[i];
@ -180,9 +187,16 @@ static FAR struct phy_notify_s *phy_find_assigned(FAR const char *intf,
pid_t pid) pid_t pid)
{ {
FAR struct phy_notify_s *client; FAR struct phy_notify_s *client;
int ret;
int i; int i;
phy_semtake(); ret = phy_semtake();
if (ret < 0)
{
phyerr("ERROR: phy_semtake failed: %d\n", ret);
return NULL;
}
for (i = 0; i < CONFIG_PHY_NOTIFICATION_NCLIENTS; i++) for (i = 0; i < CONFIG_PHY_NOTIFICATION_NCLIENTS; i++)
{ {
client = &g_notify_clients[i]; client = &g_notify_clients[i];
@ -286,7 +300,7 @@ int phy_notify_subscribe(FAR const char *intf, pid_t pid,
/* Check if this client already exists */ /* Check if this client already exists */
client = phy_find_assigned(intf, pid); client = phy_find_assigned(intf, pid);
if (client) if (client != NULL)
{ {
/* Yes.. update the signal number and argument */ /* Yes.. update the signal number and argument */
@ -297,7 +311,7 @@ int phy_notify_subscribe(FAR const char *intf, pid_t pid,
/* No, allocate a new slot in the client notification table */ /* No, allocate a new slot in the client notification table */
client = phy_find_unassigned(); client = phy_find_unassigned();
if (!client) if (client == NULL)
{ {
phyerr("ERROR: Failed to allocate a client entry\n"); phyerr("ERROR: Failed to allocate a client entry\n");
return -ENOMEM; return -ENOMEM;
@ -345,13 +359,14 @@ int phy_notify_subscribe(FAR const char *intf, pid_t pid,
int phy_notify_unsubscribe(FAR const char *intf, pid_t pid) int phy_notify_unsubscribe(FAR const char *intf, pid_t pid)
{ {
FAR struct phy_notify_s *client; FAR struct phy_notify_s *client;
int ret;
phyinfo("%s: PID=%d\n", intf, pid); phyinfo("%s: PID=%d\n", intf, pid);
/* Find the client entry for this interface */ /* Find the client entry for this interface */
client = phy_find_assigned(intf, pid); client = phy_find_assigned(intf, pid);
if (!client) if (client == NULL)
{ {
phyerr("ERROR: No such client\n"); phyerr("ERROR: No such client\n");
return -ENOENT; return -ENOENT;
@ -359,20 +374,24 @@ int phy_notify_unsubscribe(FAR const char *intf, pid_t pid)
/* Detach and disable the PHY interrupt */ /* Detach and disable the PHY interrupt */
phy_semtake(); ret = phy_semtake();
arch_phy_irq(intf, NULL, NULL, NULL); if (ret >= 0)
{
arch_phy_irq(intf, NULL, NULL, NULL);
/* Cancel any pending notification */ /* Cancel any pending notification */
nxsig_cancel_notification(&client->work); nxsig_cancel_notification(&client->work);
/* Un-initialize the client entry */ /* Un-initialize the client entry */
client->assigned = false; client->assigned = false;
client->intf[0] = '\0'; client->intf[0] = '\0';
client->pid = -1; client->pid = -1;
phy_semgive();
}
phy_semgive();
return OK; return OK;
} }

View File

@ -1,40 +1,25 @@
/**************************************************************************** /****************************************************************************
* drivers/net/slip.c * drivers/net/slip.c
* *
* Copyright (C) 2011-2012, 2015-2018 Gregory Nutt. All rights reserved. * Licensed to the Apache Software Foundation (ASF) under one or more
* Author: Gregory Nutt <gnutt@nuttx.org> * 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
* *
* Reference: RFC 1055 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Redistribution and use in source and binary forms, with or without * Unless required by applicable law or agreed to in writing, software
* modification, are permitted provided that the following conditions * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* are met: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* * License for the specific language governing permissions and limitations
* 1. Redistributions of source code must retain the above copyright * under the License.
* 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.
* *
****************************************************************************/ ****************************************************************************/
/* Reference: RFC 1055 */
/**************************************************************************** /****************************************************************************
* Included Files * Included Files
****************************************************************************/ ****************************************************************************/
@ -110,14 +95,14 @@
# define CONFIG_NET_SLIP_NINTERFACES 1 # define CONFIG_NET_SLIP_NINTERFACES 1
#endif #endif
/* SLIP special character codes *******************************************/ /* SLIP special character codes ********************************************/
#define SLIP_END 0300 /* Indicates end of packet */ #define SLIP_END 0300 /* Indicates end of packet */
#define SLIP_ESC 0333 /* Indicates byte stuffing */ #define SLIP_ESC 0333 /* Indicates byte stuffing */
#define SLIP_ESC_END 0334 /* ESC ESC_END means SLIP_END data byte */ #define SLIP_ESC_END 0334 /* ESC ESC_END means SLIP_END data byte */
#define SLIP_ESC_ESC 0335 /* ESC ESC_ESC means ESC data byte */ #define SLIP_ESC_ESC 0335 /* ESC ESC_ESC means ESC data byte */
/* General driver definitions **********************************************/ /* General driver definitions ***********************************************/
/* TX poll delay = 1 second = 1000000 microseconds. */ /* TX poll delay = 1 second = 1000000 microseconds. */
@ -167,7 +152,8 @@ static struct slip_driver_s g_slip[CONFIG_NET_SLIP_NINTERFACES];
* Private Function Prototypes * Private Function Prototypes
****************************************************************************/ ****************************************************************************/
static void slip_semtake(FAR struct slip_driver_s *priv); static int slip_semtake(FAR struct slip_driver_s *priv);
static void slip_forcetake(FAR struct slip_driver_s *priv);
/* Common TX logic */ /* Common TX logic */
@ -202,13 +188,41 @@ static int slip_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac);
* Name: slip_semtake * Name: slip_semtake
****************************************************************************/ ****************************************************************************/
static void slip_semtake(FAR struct slip_driver_s *priv) static int slip_semtake(FAR struct slip_driver_s *priv)
{ {
nxsem_wait_uninterruptible(&priv->waitsem); return nxsem_wait_uninterruptible(&priv->waitsem);
} }
#define slip_semgive(p) nxsem_post(&(p)->waitsem); #define slip_semgive(p) nxsem_post(&(p)->waitsem);
/****************************************************************************
* Name: slip_forcetake
*
* Description:
* This is just another wrapper but this one continues even if the thread
* is canceled. This must be done in certain conditions where were must
* continue in order to clean-up resources.
*
****************************************************************************/
static void slip_forcetake(FAR struct slip_driver_s *priv)
{
int ret;
do
{
ret = nxsem_wait_uninterruptible(&priv->waitsem);
/* The only expected error would -ECANCELED meaning that the
* parent thread has been canceled. We have to continue and
* terminate the poll in this case.
*/
DEBUGASSERT(ret == OK || ret == -ECANCELED);
}
while (ret < 0);
}
/**************************************************************************** /****************************************************************************
* Name: slip_write * Name: slip_write
* *
@ -384,7 +398,8 @@ static void slip_transmit(FAR struct slip_driver_s *priv)
static int slip_txpoll(FAR struct net_driver_s *dev) static int slip_txpoll(FAR struct net_driver_s *dev)
{ {
FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private; FAR struct slip_driver_s *priv =
(FAR struct slip_driver_s *)dev->d_private;
/* If the polling resulted in data that should be sent out on the network, /* If the polling resulted in data that should be sent out on the network,
* the field d_len is set to a value > 0. * the field d_len is set to a value > 0.
@ -398,8 +413,8 @@ static int slip_txpoll(FAR struct net_driver_s *dev)
} }
} }
/* If zero is returned, the polling will continue until all connections have /* If zero is returned, the polling will continue until all connections
* been examined. * have been examined.
*/ */
return 0; return 0;
@ -426,6 +441,7 @@ static int slip_txtask(int argc, FAR char *argv[])
clock_t start_ticks; clock_t start_ticks;
clock_t now_ticks; clock_t now_ticks;
unsigned int hsec; unsigned int hsec;
int ret;
nerr("index: %d\n", index); nerr("index: %d\n", index);
DEBUGASSERT(index < CONFIG_NET_SLIP_NINTERFACES); DEBUGASSERT(index < CONFIG_NET_SLIP_NINTERFACES);
@ -440,11 +456,17 @@ static int slip_txtask(int argc, FAR char *argv[])
/* Loop forever */ /* Loop forever */
start_ticks = clock_systimer(); start_ticks = clock_systimer();
for (; ; ) for (; ; )
{ {
/* Wait for the timeout to expire (or until we are signaled by */ /* Wait for the timeout to expire (or until we are signaled by */
slip_semtake(priv); ret = slip_semtake(priv);
if (ret < 0)
{
DEBUGASSERT(ret == -ECANCELED);
break;
}
if (!priv->txnodelay) if (!priv->txnodelay)
{ {
slip_semgive(priv); slip_semgive(priv);
@ -777,7 +799,8 @@ static int slip_rxtask(int argc, FAR char *argv[])
static int slip_ifup(FAR struct net_driver_s *dev) static int slip_ifup(FAR struct net_driver_s *dev)
{ {
FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private; FAR struct slip_driver_s *priv =
(FAR struct slip_driver_s *)dev->d_private;
#ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv4
nerr("Bringing up: %d.%d.%d.%d\n", nerr("Bringing up: %d.%d.%d.%d\n",
@ -815,7 +838,8 @@ static int slip_ifup(FAR struct net_driver_s *dev)
static int slip_ifdown(FAR struct net_driver_s *dev) static int slip_ifdown(FAR struct net_driver_s *dev)
{ {
FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private; FAR struct slip_driver_s *priv =
(FAR struct slip_driver_s *)dev->d_private;
/* Mark the device "down" */ /* Mark the device "down" */
@ -841,7 +865,8 @@ static int slip_ifdown(FAR struct net_driver_s *dev)
static int slip_txavail(FAR struct net_driver_s *dev) static int slip_txavail(FAR struct net_driver_s *dev)
{ {
FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private; FAR struct slip_driver_s *priv =
(FAR struct slip_driver_s *)dev->d_private;
/* Ignore the notification if the interface is not yet up */ /* Ignore the notification if the interface is not yet up */
@ -877,7 +902,8 @@ static int slip_txavail(FAR struct net_driver_s *dev)
#ifdef CONFIG_NET_MCASTGROUP #ifdef CONFIG_NET_MCASTGROUP
static int slip_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) static int slip_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
{ {
FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private; FAR struct slip_driver_s *priv =
(FAR struct slip_driver_s *)dev->d_private;
/* Add the MAC address to the hardware multicast routing table */ /* Add the MAC address to the hardware multicast routing table */
@ -906,7 +932,8 @@ static int slip_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
#ifdef CONFIG_NET_MCASTGROUP #ifdef CONFIG_NET_MCASTGROUP
static int slip_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) static int slip_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
{ {
FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private; FAR struct slip_driver_s *priv =
(FAR struct slip_driver_s *)dev->d_private;
/* Add the MAC address to the hardware multicast routing table */ /* Add the MAC address to the hardware multicast routing table */
@ -993,7 +1020,7 @@ int slip_initialize(int intf, FAR const char *devname)
/* Wait and make sure that the receive task is started. */ /* Wait and make sure that the receive task is started. */
slip_semtake(priv); slip_forcetake(priv);
/* Start the SLIP transmitter kernel thread */ /* Start the SLIP transmitter kernel thread */
@ -1008,7 +1035,7 @@ int slip_initialize(int intf, FAR const char *devname)
/* Wait and make sure that the transmit task is started. */ /* Wait and make sure that the transmit task is started. */
slip_semtake(priv); slip_forcetake(priv);
/* Bump the semaphore count so that it can now be used as a mutex */ /* Bump the semaphore count so that it can now be used as a mutex */

View File

@ -819,7 +819,8 @@ static ssize_t telnet_read(FAR struct file *filep, FAR char *buffer,
{ {
FAR struct inode *inode = filep->f_inode; FAR struct inode *inode = filep->f_inode;
FAR struct telnet_dev_s *priv = inode->i_private; FAR struct telnet_dev_s *priv = inode->i_private;
ssize_t ret = 0; ssize_t nread = 0;
int ret;
ninfo("len: %d\n", len); ninfo("len: %d\n", len);
@ -848,33 +849,44 @@ static ssize_t telnet_read(FAR struct file *filep, FAR char *buffer,
return -EAGAIN; return -EAGAIN;
} }
/* Wait for new data (or error) */ /* Wait for new data, interrupt, or thread cancellation */
ret = nxsem_wait(&priv->td_iosem);
if (ret < 0)
{
nerr("ERROR: nxsem_wait failed: %d\n", ret);
return (ssize_t)ret;
}
nxsem_wait_uninterruptible(&priv->td_iosem);
continue; continue;
} }
/* Take exclusive access to data buffer */ /* Take exclusive access to data buffer */
nxsem_wait(&priv->td_exclsem); ret = nxsem_wait(&priv->td_exclsem);
if (ret < 0)
{
nerr("ERROR: nxsem_wait failed: %d\n", ret);
return (ssize_t)ret;
}
/* Process the buffered telnet data */ /* Process the buffered telnet data */
src = &priv->td_rxbuffer[priv->td_offset]; src = &priv->td_rxbuffer[priv->td_offset];
ret = telnet_receive(priv, src, priv->td_pending, buffer, len); nread = telnet_receive(priv, src, priv->td_pending, buffer, len);
nxsem_post(&priv->td_exclsem); nxsem_post(&priv->td_exclsem);
} }
while (ret == 0); while (nread == 0);
/* Returned Value: /* Returned Value:
* *
* ret > 0: The number of characters copied into the user buffer by * nread > 0: The number of characters copied into the user buffer by
* telnet_receive(). * telnet_receive().
* ret <= 0: Loss of connection or error events reported by recv(). * nread <= 0: Loss of connection or error events reported by recv().
*/ */
return ret; return nread;
} }
/**************************************************************************** /****************************************************************************

View File

@ -166,7 +166,7 @@ struct tun_driver_s
* Private Function Prototypes * Private Function Prototypes
****************************************************************************/ ****************************************************************************/
static void tun_lock(FAR struct tun_device_s *priv); static int tun_lock(FAR struct tun_device_s *priv);
static void tun_unlock(FAR struct tun_device_s *priv); static void tun_unlock(FAR struct tun_device_s *priv);
/* Common TX logic */ /* Common TX logic */
@ -249,9 +249,9 @@ static const struct file_operations g_tun_file_ops =
* Name: tundev_lock * Name: tundev_lock
****************************************************************************/ ****************************************************************************/
static void tundev_lock(FAR struct tun_driver_s *tun) static int tundev_lock(FAR struct tun_driver_s *tun)
{ {
nxsem_wait_uninterruptible(&tun->waitsem); return nxsem_wait_uninterruptible(&tun->waitsem);
} }
/**************************************************************************** /****************************************************************************
@ -267,9 +267,9 @@ static void tundev_unlock(FAR struct tun_driver_s *tun)
* Name: tun_lock * Name: tun_lock
****************************************************************************/ ****************************************************************************/
static void tun_lock(FAR struct tun_device_s *priv) static int tun_lock(FAR struct tun_device_s *priv)
{ {
nxsem_wait_uninterruptible(&priv->waitsem); return nxsem_wait_uninterruptible(&priv->waitsem);
} }
/**************************************************************************** /****************************************************************************
@ -778,10 +778,21 @@ static void tun_txdone(FAR struct tun_device_s *priv)
static void tun_poll_work(FAR void *arg) static void tun_poll_work(FAR void *arg)
{ {
FAR struct tun_device_s *priv = (FAR struct tun_device_s *)arg; FAR struct tun_device_s *priv = (FAR struct tun_device_s *)arg;
int ret;
/* Perform the poll */ /* Perform the poll */
tun_lock(priv); ret = tun_lock(priv);
if (ret < 0)
{
/* This would indicate that the worker thread was canceled.. not a
* likely event.
*/
DEBUGASSERT(ret == -ECANCELED);
return;
}
net_lock(); net_lock();
/* Check if there is room in the send another TX packet. We cannot perform /* Check if there is room in the send another TX packet. We cannot perform
@ -930,8 +941,15 @@ static int tun_ifdown(FAR struct net_driver_s *dev)
static void tun_txavail_work(FAR void *arg) static void tun_txavail_work(FAR void *arg)
{ {
FAR struct tun_device_s *priv = (FAR struct tun_device_s *)arg; FAR struct tun_device_s *priv = (FAR struct tun_device_s *)arg;
int ret;
tun_lock(priv); ret = tun_lock(priv);
if (ret < 0)
{
/* Thread has been canceled, skip poll-related work */
return;
}
/* Check if there is room to hold another network packet. */ /* Check if there is room to hold another network packet. */
@ -1152,6 +1170,7 @@ static int tun_close(FAR struct file *filep)
FAR struct tun_driver_s *tun = inode->i_private; FAR struct tun_driver_s *tun = inode->i_private;
FAR struct tun_device_s *priv = filep->f_priv; FAR struct tun_device_s *priv = filep->f_priv;
int intf; int intf;
int ret;
if (priv == NULL) if (priv == NULL)
{ {
@ -1159,13 +1178,16 @@ static int tun_close(FAR struct file *filep)
} }
intf = priv - g_tun_devices; intf = priv - g_tun_devices;
tundev_lock(tun); ret = tundev_lock(tun);
if (ret >= 0)
{
tun->free_tuns |= (1 << intf);
tun_dev_uninit(priv);
tun->free_tuns |= (1 << intf); tundev_unlock(tun);
tun_dev_uninit(priv); }
tundev_unlock(tun); return ret;
return OK;
} }
/**************************************************************************** /****************************************************************************
@ -1176,17 +1198,26 @@ static ssize_t tun_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen) size_t buflen)
{ {
FAR struct tun_device_s *priv = filep->f_priv; FAR struct tun_device_s *priv = filep->f_priv;
ssize_t ret; ssize_t nwritten = 0;
int ret;
if (priv == NULL || buflen > CONFIG_NET_TUN_PKTSIZE) if (priv == NULL || buflen > CONFIG_NET_TUN_PKTSIZE)
{ {
return -EINVAL; return -EINVAL;
} }
tun_lock(priv);
for (; ; ) for (; ; )
{ {
/* Write must return immediately if interrupted by a signal (or if the
* thread is canceled) and no data has yet been written.
*/
ret = nxsem_wait(&priv->waitsem);
if (ret < 0)
{
return nwritten == 0 ? (ssize_t)ret : nwritten;
}
/* Check if there are free space to write */ /* Check if there are free space to write */
if (priv->write_d_len == 0) if (priv->write_d_len == 0)
@ -1200,7 +1231,7 @@ static ssize_t tun_write(FAR struct file *filep, FAR const char *buffer,
tun_net_receive(priv); tun_net_receive(priv);
net_unlock(); net_unlock();
ret = buflen; nwritten = buflen;
break; break;
} }
@ -1208,18 +1239,17 @@ static ssize_t tun_write(FAR struct file *filep, FAR const char *buffer,
if ((filep->f_oflags & O_NONBLOCK) != 0) if ((filep->f_oflags & O_NONBLOCK) != 0)
{ {
ret = -EAGAIN; nwritten = -EAGAIN;
break; break;
} }
priv->write_wait = true; priv->write_wait = true;
tun_unlock(priv); tun_unlock(priv);
nxsem_wait(&priv->write_wait_sem); nxsem_wait(&priv->write_wait_sem);
tun_lock(priv);
} }
tun_unlock(priv); tun_unlock(priv);
return ret; return nwritten;
} }
/**************************************************************************** /****************************************************************************
@ -1230,29 +1260,38 @@ static ssize_t tun_read(FAR struct file *filep, FAR char *buffer,
size_t buflen) size_t buflen)
{ {
FAR struct tun_device_s *priv = filep->f_priv; FAR struct tun_device_s *priv = filep->f_priv;
ssize_t ret; ssize_t nread;
int ret;
if (priv == NULL) if (priv == NULL)
{ {
return -EINVAL; return -EINVAL;
} }
tun_lock(priv);
for (; ; ) for (; ; )
{ {
/* Read must return immediately if interrupted by a signal (or if the
* thread is canceled) and no data has yet been read.
*/
ret = nxsem_wait(&priv->waitsem);
if (ret < 0)
{
return nread == 0 ? (ssize_t)ret : nread;
}
/* Check if there are data to read in write buffer */ /* Check if there are data to read in write buffer */
if (priv->write_d_len > 0) if (priv->write_d_len > 0)
{ {
if (buflen < priv->write_d_len) if (buflen < priv->write_d_len)
{ {
ret = -EINVAL; nread = -EINVAL;
break; break;
} }
memcpy(buffer, priv->write_buf, priv->write_d_len); memcpy(buffer, priv->write_buf, priv->write_d_len);
ret = priv->write_d_len; nread = priv->write_d_len;
priv->write_d_len = 0; priv->write_d_len = 0;
NETDEV_TXDONE(&priv->dev); NETDEV_TXDONE(&priv->dev);
@ -1266,12 +1305,12 @@ static ssize_t tun_read(FAR struct file *filep, FAR char *buffer,
{ {
if (buflen < priv->read_d_len) if (buflen < priv->read_d_len)
{ {
ret = -EINVAL; nread = -EINVAL;
break; break;
} }
memcpy(buffer, priv->read_buf, priv->read_d_len); memcpy(buffer, priv->read_buf, priv->read_d_len);
ret = priv->read_d_len; nread = priv->read_d_len;
priv->read_d_len = 0; priv->read_d_len = 0;
net_lock(); net_lock();
@ -1284,18 +1323,17 @@ static ssize_t tun_read(FAR struct file *filep, FAR char *buffer,
if ((filep->f_oflags & O_NONBLOCK) != 0) if ((filep->f_oflags & O_NONBLOCK) != 0)
{ {
ret = -EAGAIN; nread = -EAGAIN;
break; break;
} }
priv->read_wait = true; priv->read_wait = true;
tun_unlock(priv); tun_unlock(priv);
nxsem_wait(&priv->read_wait_sem); nxsem_wait(&priv->read_wait_sem);
tun_lock(priv);
} }
tun_unlock(priv); tun_unlock(priv);
return ret; return nread;
} }
/**************************************************************************** /****************************************************************************
@ -1306,7 +1344,7 @@ int tun_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
{ {
FAR struct tun_device_s *priv = filep->f_priv; FAR struct tun_device_s *priv = filep->f_priv;
pollevent_t eventset; pollevent_t eventset;
int ret = OK; int ret;
/* Some sanity checking */ /* Some sanity checking */
@ -1315,7 +1353,11 @@ int tun_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
return -EINVAL; return -EINVAL;
} }
tun_lock(priv); ret = tun_lock(priv);
if (ret < 0)
{
return ret;
}
if (setup) if (setup)
{ {
@ -1384,7 +1426,11 @@ static int tun_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
return -EINVAL; return -EINVAL;
} }
tundev_lock(tun); ret = tundev_lock(tun);
if (ret < 0)
{
return ret;
}
free_tuns = tun->free_tuns; free_tuns = tun->free_tuns;

View File

@ -1,36 +1,20 @@
/**************************************************************************** /****************************************************************************
* drivers/rwbuffer.c * drivers/rwbuffer.c
* *
* Copyright (C) 2009, 2011, 2013-2014, 2017, 2020 Gregory Nutt. All * Licensed to the Apache Software Foundation (ASF) under one or more
* rights reserved. * contributor license agreements. See the NOTICE file distributed with
* Author: Gregory Nutt <gnutt@nuttx.org> * 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
* *
* Redistribution and use in source and binary forms, with or without * http://www.apache.org/licenses/LICENSE-2.0
* modification, are permitted provided that the following conditions
* are met:
* *
* 1. Redistributions of source code must retain the above copyright * Unless required by applicable law or agreed to in writing, software
* notice, this list of conditions and the following disclaimer. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* 2. Redistributions in binary form must reproduce the above copyright * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* notice, this list of conditions and the following disclaimer in * License for the specific language governing permissions and limitations
* the documentation and/or other materials provided with the * under the License.
* 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.
* *
****************************************************************************/ ****************************************************************************/
@ -79,9 +63,9 @@
* Name: rwb_semtake * Name: rwb_semtake
****************************************************************************/ ****************************************************************************/
static void rwb_semtake(FAR sem_t *sem) static int rwb_semtake(FAR sem_t *sem)
{ {
nxsem_wait_uninterruptible(sem); return nxsem_wait_uninterruptible(sem);
} }
/**************************************************************************** /****************************************************************************
@ -90,6 +74,34 @@ static void rwb_semtake(FAR sem_t *sem)
#define rwb_semgive(s) nxsem_post(s) #define rwb_semgive(s) nxsem_post(s)
/****************************************************************************
* Name: rwb_forcetake
*
* Description:
* This is just another wrapper but this one continues even if the thread
* is canceled. This must be done in certain conditions where were must
* continue in order to clean-up resources.
*
****************************************************************************/
static void rwb_forcetake(FAR sem_t *sem)
{
int ret;
do
{
ret = nxsem_wait_uninterruptible(sem);
/* The only expected error would -ECANCELED meaning that the
* parent thread has been canceled. We have to continue and
* terminate the poll in this case.
*/
DEBUGASSERT(ret == OK || ret == -ECANCELED);
}
while (ret < 0);
}
/**************************************************************************** /****************************************************************************
* Name: rwb_overlap * Name: rwb_overlap
****************************************************************************/ ****************************************************************************/
@ -182,7 +194,7 @@ static void rwb_wrtimeout(FAR void *arg)
* worker thread. * worker thread.
*/ */
rwb_semtake(&rwb->wrsem); rwb_forcetake(&rwb->wrsem);
rwb_wrflush(rwb); rwb_wrflush(rwb);
rwb_semgive(&rwb->wrsem); rwb_semgive(&rwb->wrsem);
} }
@ -410,7 +422,7 @@ int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb,
finfo("startblock=%d blockcount=%p\n", startblock, blockcount); finfo("startblock=%d blockcount=%p\n", startblock, blockcount);
rwb_semtake(&rwb->wrsem); rwb_forcetake(&rwb->wrsem);
/* Now there are five cases: /* Now there are five cases:
* *
@ -547,7 +559,7 @@ int rwb_invalidate_readahead(FAR struct rwbuffer_s *rwb,
finfo("startblock=%d blockcount=%p\n", startblock, blockcount); finfo("startblock=%d blockcount=%p\n", startblock, blockcount);
rwb_semtake(&rwb->rhsem); rwb_forcetake(&rwb->rhsem);
/* Now there are five cases: /* Now there are five cases:
* *
@ -781,7 +793,14 @@ static ssize_t rwb_read_(FAR struct rwbuffer_s *rwb, off_t startblock,
/* Loop until we have read all of the requested blocks */ /* Loop until we have read all of the requested blocks */
rwb_semtake(&rwb->rhsem); ret = nxsem_wait(&rwb->rhsem);
if (ret < 0)
{
/* Return EINTR or ECANCELED */
return ret;
}
for (remaining = nblocks; remaining > 0; ) for (remaining = nblocks; remaining > 0; )
{ {
/* Is there anything in the read-ahead buffer? */ /* Is there anything in the read-ahead buffer? */