mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
pcm: Add the PCM state checks to plugins
I noticed that some plugin codes have no proper PCM state checks and it results in expected outcomes. For example, when snd_pcm_drain() is called for a dmix PCM after calling snd_pcm_drop(), it stalls unexpectedly. It's just because its drain callback doesn't expect the SND_PCM_SETUP state. We can fix such a bug in each place one by one, but a safer way would be to filter out all such cases commonly in the PCM API functions themselves. This patch adds the PCM state sanity checks to major API functions so that they return -EBADFD when called in the unexpected PCM states. As well as for the thread-safety extension, it'd be a question of the performance; again at this time, the hw PCM is considered as an exception, and it has pcm->own_state_check flag set, which means that the common PCM state checks are skipped. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
3e4887c112
commit
ea74ebbe4d
3 changed files with 50 additions and 0 deletions
|
|
@ -655,6 +655,23 @@ playback devices.
|
|||
#include <limits.h>
|
||||
#include "pcm_local.h"
|
||||
|
||||
#ifndef DOC_HIDDEN
|
||||
#define P_STATE(x) (1U << SND_PCM_STATE_ ## x)
|
||||
#define P_STATE_RUNNABLE (P_STATE(PREPARED) | \
|
||||
P_STATE(RUNNING) | \
|
||||
P_STATE(XRUN) | \
|
||||
P_STATE(PAUSED) | \
|
||||
P_STATE(DRAINING))
|
||||
|
||||
/* check whether the PCM is in the unexpected state */
|
||||
static int bad_pcm_state(snd_pcm_t *pcm, unsigned int supported_states)
|
||||
{
|
||||
if (pcm->own_state_check)
|
||||
return 0; /* don't care, the plugin checks by itself */
|
||||
return !(supported_states & (1U << snd_pcm_state(pcm)));
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief get identifier of PCM handle
|
||||
* \param pcm PCM handle
|
||||
|
|
@ -1120,6 +1137,8 @@ int snd_pcm_prepare(snd_pcm_t *pcm)
|
|||
SNDMSG("PCM not set up");
|
||||
return -EIO;
|
||||
}
|
||||
if (bad_pcm_state(pcm, ~P_STATE(DISCONNECTED)))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
err = pcm->fast_ops->prepare(pcm->fast_op_arg);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
@ -1166,6 +1185,8 @@ int snd_pcm_start(snd_pcm_t *pcm)
|
|||
SNDMSG("PCM not set up");
|
||||
return -EIO;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE(PREPARED)))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
err = __snd_pcm_start(pcm);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
@ -1194,6 +1215,9 @@ int snd_pcm_drop(snd_pcm_t *pcm)
|
|||
SNDMSG("PCM not set up");
|
||||
return -EIO;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE | P_STATE(SETUP) |
|
||||
P_STATE(SUSPENDED)))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
err = pcm->fast_ops->drop(pcm->fast_op_arg);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
@ -1222,6 +1246,8 @@ int snd_pcm_drain(snd_pcm_t *pcm)
|
|||
SNDMSG("PCM not set up");
|
||||
return -EIO;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
/* lock handled in the callback */
|
||||
return pcm->fast_ops->drain(pcm->fast_op_arg);
|
||||
}
|
||||
|
|
@ -1247,6 +1273,8 @@ int snd_pcm_pause(snd_pcm_t *pcm, int enable)
|
|||
SNDMSG("PCM not set up");
|
||||
return -EIO;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
err = pcm->fast_ops->pause(pcm->fast_op_arg, enable);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
@ -1273,6 +1301,8 @@ snd_pcm_sframes_t snd_pcm_rewindable(snd_pcm_t *pcm)
|
|||
SNDMSG("PCM not set up");
|
||||
return -EIO;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
result = pcm->fast_ops->rewindable(pcm->fast_op_arg);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
@ -1299,6 +1329,8 @@ snd_pcm_sframes_t snd_pcm_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|||
}
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
result = pcm->fast_ops->rewind(pcm->fast_op_arg, frames);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
@ -1325,6 +1357,8 @@ snd_pcm_sframes_t snd_pcm_forwardable(snd_pcm_t *pcm)
|
|||
SNDMSG("PCM not set up");
|
||||
return -EIO;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
result = pcm->fast_ops->forwardable(pcm->fast_op_arg);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
@ -1355,6 +1389,8 @@ snd_pcm_sframes_t snd_pcm_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|||
}
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
result = pcm->fast_ops->forward(pcm->fast_op_arg, frames);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
@ -1393,6 +1429,8 @@ snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_ufr
|
|||
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
return _snd_pcm_writei(pcm, buffer, size);
|
||||
}
|
||||
|
||||
|
|
@ -1427,6 +1465,8 @@ snd_pcm_sframes_t snd_pcm_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t
|
|||
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
return _snd_pcm_writen(pcm, bufs, size);
|
||||
}
|
||||
|
||||
|
|
@ -1461,6 +1501,8 @@ snd_pcm_sframes_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t
|
|||
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
return _snd_pcm_readi(pcm, buffer, size);
|
||||
}
|
||||
|
||||
|
|
@ -1495,6 +1537,8 @@ snd_pcm_sframes_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t s
|
|||
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
return _snd_pcm_readn(pcm, bufs, size);
|
||||
}
|
||||
|
||||
|
|
@ -6938,6 +6982,8 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm,
|
|||
{
|
||||
int err;
|
||||
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
err = __snd_pcm_mmap_begin(pcm, areas, offset, frames);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
@ -7033,6 +7079,8 @@ snd_pcm_sframes_t snd_pcm_mmap_commit(snd_pcm_t *pcm,
|
|||
{
|
||||
snd_pcm_sframes_t result;
|
||||
|
||||
if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
|
||||
return -EBADFD;
|
||||
snd_pcm_lock(pcm);
|
||||
result = __snd_pcm_mmap_commit(pcm, offset, frames);
|
||||
snd_pcm_unlock(pcm);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue