PCM: allow mmap-access conversion in plug

The plug plugin has a long-standing problem that it can handle only
slaves that support mmap because of format/rate/access conversions
(these corresponding plugins work only with mmap).

This patch adds the support of automatic mmap->rw conversion via
mmap_emul plugin.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2008-06-06 17:10:41 +02:00
parent 538726cc55
commit 1467f4e238
2 changed files with 79 additions and 10 deletions

View file

@ -520,6 +520,9 @@ fi
if test "$build_pcm_alaw" = "yes"; then
AC_DEFINE([BUILD_PCM_PLUGIN_ALAW], "1", [Build PCM alaw plugin])
fi
if test "$build_pcm_mmap_emul" = "yes"; then
AC_DEFINE([BUILD_PCM_PLUGIN_MMAP_EMUL], "1", [Build PCM mmap-emul plugin])
fi
dnl Create PCM plugin symbol list for static library

View file

@ -581,12 +581,44 @@ static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_p
return 1;
}
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
static int snd_pcm_plug_change_mmap(snd_pcm_t *pcm, snd_pcm_t **new,
snd_pcm_plug_params_t *clt,
snd_pcm_plug_params_t *slv)
{
snd_pcm_plug_t *plug = pcm->private_data;
int err;
if (clt->access == slv->access)
return 0;
switch (slv->access) {
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
case SND_PCM_ACCESS_MMAP_COMPLEX:
return 0;
default:
break;
}
err = __snd_pcm_mmap_emul_open(new, NULL, plug->gen.slave,
plug->gen.slave != plug->req_slave);
if (err < 0)
return err;
slv->access = clt->access;
return 1;
}
#endif
static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
snd_pcm_plug_params_t *client,
snd_pcm_plug_params_t *slave)
{
snd_pcm_plug_t *plug = pcm->private_data;
static int (*funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
snd_pcm_plug_change_mmap,
#endif
snd_pcm_plug_change_format,
#ifdef BUILD_PCM_PLUGIN_ROUTE
snd_pcm_plug_change_channels,
@ -693,6 +725,43 @@ static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *
return 0;
}
static int check_access_change(snd_pcm_hw_params_t *cparams,
snd_pcm_hw_params_t *sparams)
{
snd_pcm_access_mask_t *smask;
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
const snd_pcm_access_mask_t *cmask;
snd_pcm_access_mask_t mask;
#endif
smask = (snd_pcm_access_mask_t *)
snd_pcm_hw_param_get_mask(sparams,
SND_PCM_HW_PARAM_ACCESS);
if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_COMPLEX))
return 0; /* OK, we have mmap support */
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
/* no mmap support - we need mmap emulation */
cmask = (const snd_pcm_access_mask_t *)
snd_pcm_hw_param_get_mask(cparams,
SND_PCM_HW_PARAM_ACCESS);
snd_mask_none(&mask);
if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
snd_pcm_access_mask_set(&mask,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
snd_pcm_access_mask_set(&mask,
SND_PCM_ACCESS_RW_NONINTERLEAVED);
*smask = mask;
return 0;
#else
return -EINVAL;
#endif
}
static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
snd_pcm_hw_params_t *sparams)
{
@ -764,18 +833,15 @@ static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *p
return -EINVAL;
}
if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_FORMAT, sparams) ||
snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_CHANNELS, sparams) ||
snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_RATE, sparams) ||
snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_MMAP };
_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
&access_mask);
if (snd_pcm_access_mask_empty(snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS))) {
SNDERR("Unable to find an usable access for '%s'", pcm->name);
return -EINVAL;
if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
err = check_access_change(params, sparams);
if (err < 0) {
SNDERR("Unable to find an usable access for '%s'",
pcm->name);
return err;
}
}
if ((links & SND_PCM_HW_PARBIT_RATE) ||
snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |