blob: abd5eca46e289a1e630e76e9ba34ecd0e476f087 [file] [log] [blame]
/*
* SALSA-Lib - PCM HW/SW PARAMS
*
* Copyright (c) 2007-2012 by Takashi Iwai <tiwai@suse.de>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <limits.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "pcm.h"
#include "control.h"
#include "local.h"
/*
* non-inlined mask operations
*/
#define MASK_SIZE (SND_MASK_MAX / 32)
#define MASK_OFS(i) ((i) >> 5)
#define MASK_BIT(i) (1U << ((i) & 31))
#define mask_set_any(m) _snd_mask_any(m)
#define mask_get(m, val) _snd_mask_test(m, val)
#define mask_set(m, val) _snd_mask_set(m, val)
#define mask_reset(m, val) _snd_mask_reset(m, val)
#define mask_is_empty(m) _snd_mask_empty(m)
#define mask_clear(m) _snd_mask_none(m)
static int mask_is_single(const snd_mask_t *mask)
{
int i, c = 0;
for (i = 0; i < MASK_SIZE; i++) {
if (!mask->bits[i])
continue;
if (mask->bits[i] & (mask->bits[i] - 1))
return 0;
if (c)
return 0;
c++;
}
return 1;
}
static void mask_leave_single(snd_mask_t *mask, unsigned int val)
{
unsigned int v;
v = mask_get(mask, val);
mask_clear(mask);
mask->bits[MASK_OFS(val)] = v;
}
static void mask_reset_range(snd_mask_t *mask, unsigned int from,
unsigned int to)
{
for (; from <= to; from++)
mask_reset(mask, from);
}
static unsigned int ld2(u_int32_t v)
{
unsigned int r, bits;
for (r = 0, bits = 16; v > 1; bits >>= 1) {
if (v >= (1U << bits)) {
v >>= bits;
r += bits;
}
}
return r;
}
static unsigned int mask_get_max(const snd_mask_t *mask)
{
int i;
for (i = MASK_SIZE - 1; i >= 0; i--) {
if (mask->bits[i])
return ld2(mask->bits[i]) + (i << 5);
}
return 0;
}
static unsigned int mask_get_min(const snd_mask_t *mask)
{
int i;
for (i = 0; i < MASK_SIZE; i++) {
if (mask->bits[i])
return ffs(mask->bits[i]) - 1 + (i << 5);
}
return 0;
}
#define mask_get_final(mask) mask_get_min(mask)
static int mask_is_full(const snd_mask_t *mask)
{
int i;
for (i = 0; i < MASK_SIZE; i++)
if (mask->bits[i] != (unsigned int)-1)
return 0;
return 1;
}
static int snd_mask_refine(snd_mask_t *mask, const snd_mask_t *v)
{
int i;
snd_mask_t old = *mask;
for (i = 0; i < MASK_SIZE; i++)
mask->bits[i] &= v->bits[i];
if (mask_is_empty(mask))
return -EINVAL;
return memcmp(mask, &old, sizeof(old));
}
static int snd_mask_refine_first(snd_mask_t *mask)
{
if (mask_is_empty(mask))
return -ENOENT;
if (mask_is_single(mask))
return 0;
mask_leave_single(mask, mask_get_min(mask));
return 1;
}
static int snd_mask_refine_last(snd_mask_t *mask)
{
if (mask_is_empty(mask))
return -ENOENT;
if (mask_is_single(mask))
return 0;
mask_leave_single(mask, mask_get_max(mask));
return 1;
}
static int snd_mask_refine_min(snd_mask_t *mask, unsigned int val)
{
if (mask_is_empty(mask))
return -ENOENT;
if (mask_get_min(mask) >= val)
return 0;
mask_reset_range(mask, 0, val - 1);
if (mask_is_empty(mask))
return -EINVAL;
return 1;
}
static int snd_mask_refine_max(snd_mask_t *mask, unsigned int val)
{
if (mask_is_empty(mask))
return -ENOENT;
if (mask_get_max(mask) <= val)
return 0;
mask_reset_range(mask, val + 1, SND_MASK_MAX - 1);
if (mask_is_empty(mask))
return -EINVAL;
return 1;
}
static int snd_mask_refine_set(snd_mask_t *mask, unsigned int val)
{
int changed;
if (mask_is_empty(mask))
return -ENOENT;
changed = !mask_is_single(mask);
mask_leave_single(mask, val);
if (mask_is_empty(mask))
return -EINVAL;
return changed;
}
/*
* Operations for interval types
*/
static void interval_set_any(snd_interval_t *i)
{
i->min = 0;
i->openmin = 0;
i->max = -1;
i->openmax = 0;
i->integer = 0;
i->empty = 0;
}
#define interval_is_empty(i) (i)->empty
static inline int interval_is_full(const snd_interval_t *i)
{
return i->min == 0 && i->openmin == 0 &&
i->max == -1 && i->openmax == 0;
}
static inline int interval_is_single(const snd_interval_t *i)
{
return (i->min == i->max ||
(i->min + 1 == i->max && i->openmax));
}
#define interval_get_min(i) (i)->min
#define interval_get_max(i) (i)->max
#define interval_get_final(i) interval_get_min(i)
static int interval_check_empty(snd_interval_t *i)
{
if (i->min > i->max ||
(i->min == i->max && (i->openmin || i->openmax))) {
i->empty = 1;
return -1;
}
return 0;
}
static int snd_interval_refine_min(snd_interval_t *i, unsigned int min,
int openmin, int integer)
{
int changed = 0;
if (interval_is_empty(i))
return -ENOENT;
if (i->min < min) {
i->min = min;
i->openmin = openmin;
changed = 1;
} else if (i->min == min && !i->openmin && openmin) {
i->openmin = 1;
changed = 1;
}
if (!i->integer && integer) {
i->integer = 1;
changed = 1;
}
if (i->integer) {
if (i->openmin) {
i->min++;
i->openmin = 0;
}
}
if (interval_check_empty(i))
return -EINVAL;
return changed;
}
static int snd_interval_refine_max(snd_interval_t *i, unsigned int max,
int openmax, int integer)
{
int changed = 0;
if (interval_is_empty(i))
return -ENOENT;
if (i->max > max) {
i->max = max;
i->openmax = openmax;
changed = 1;
} else if (i->max == max && !i->openmax && openmax) {
i->openmax = 1;
changed = 1;
}
if (!i->integer && integer) {
i->integer = 1;
changed = 1;
}
if (i->integer) {
if (i->openmax) {
i->max--;
i->openmax = 0;
}
}
if (interval_check_empty(i))
return -EINVAL;
return changed;
}
static int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v)
{
int err, changed;
err = snd_interval_refine_min(i, v->min, v->openmin, v->integer);
if (err < 0)
return err;
changed = err;
err = snd_interval_refine_max(i, v->max, v->openmax, v->integer);
if (err < 0)
return err;
changed |= err;
if (!i->integer && !i->openmin && !i->openmax && i->min == i->max)
i->integer = 1;
return changed;
}
static int snd_interval_refine_first(snd_interval_t *i)
{
if (interval_is_empty(i))
return -ENOENT;
if (interval_is_single(i))
return 0;
i->max = i->min;
i->openmax = i->openmin;
if (i->openmax)
i->max++;
return 1;
}
static int snd_interval_refine_last(snd_interval_t *i)
{
if (interval_is_empty(i))
return -ENOENT;
if (interval_is_single(i))
return 0;
i->min = i->max;
i->openmin = i->openmax;
if (i->openmin)
i->min--;
return 1;
}
static int snd_interval_refine_set(snd_interval_t *i, unsigned int val)
{
snd_interval_t t;
t.empty = 0;
t.min = t.max = val;
t.openmin = t.openmax = 0;
t.integer = 1;
return snd_interval_refine(i, &t);
}
/*
* HANDLE HW_PARAMS, SW_PARAMS
*/
/* set up default sw_params values */
static int snd_pcm_sw_params_default(snd_pcm_t *pcm,
snd_pcm_sw_params_t *params)
{
params->tstamp_mode = SND_PCM_TSTAMP_NONE;
params->tstamp_type = pcm->sw_params.tstamp_type;
params->period_step = 1;
params->sleep_min = 0;
params->avail_min = pcm->period_size;
params->xfer_align = pcm->period_size;
params->start_threshold = 1;
params->stop_threshold = pcm->buffer_size;
params->silence_threshold = 0;
params->silence_size = 0;
params->boundary = pcm->buffer_size;
while (params->boundary * 2 <= LONG_MAX - pcm->buffer_size)
params->boundary *= 2;
return 0;
}
static inline int hw_is_mask(int var)
{
return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK &&
var <= SNDRV_PCM_HW_PARAM_LAST_MASK;
}
static inline int hw_is_interval(int var)
{
return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL &&
var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL;
}
static inline
snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params,
int var)
{
return (snd_mask_t*)&params->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK];
}
static inline
snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
int var)
{
return &params->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
}
static inline
const snd_mask_t *hw_param_mask_c(const snd_pcm_hw_params_t *params,
int var)
{
return (const snd_mask_t *)hw_param_mask((snd_pcm_hw_params_t*) params, var);
}
static inline
const snd_interval_t *hw_param_interval_c(const snd_pcm_hw_params_t *params,
int var)
{
return (const snd_interval_t *)hw_param_interval((snd_pcm_hw_params_t*) params, var);
}
/*
*/
static void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params,
int var)
{
if (hw_is_mask(var))
mask_set_any(hw_param_mask(params, var));
else
interval_set_any(hw_param_interval(params, var));
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
static int snd_pcm_hw_param_empty(const snd_pcm_hw_params_t *params,
int var)
{
if (hw_is_mask(var))
return mask_is_empty(hw_param_mask_c(params, var));
else
return interval_is_empty(hw_param_interval_c(params, var));
}
static void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params)
{
unsigned int k;
memset(params, 0, sizeof(*params));
for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK;
k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++)
_snd_pcm_hw_param_any(params, k);
for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
_snd_pcm_hw_param_any(params, k);
params->rmask = ~0U;
params->cmask = 0;
params->info = ~0U;
params->flags = SNDRV_PCM_HW_PARAMS_NORESAMPLE;
}
int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
_snd_pcm_hw_params_any(params);
return snd_pcm_hw_refine(pcm, params);
}
int _snd_pcm_hw_param_get(const snd_pcm_hw_params_t *params,
int var, unsigned int *val, int *dir)
{
if (hw_is_mask(var)) {
const snd_mask_t *mask = hw_param_mask_c(params, var);
if (mask_is_empty(mask) || !mask_is_single(mask))
return -EINVAL;
if (dir)
*dir = 0;
if (val)
*val = mask_get_final(mask);
return 0;
} else if (hw_is_interval(var)) {
const snd_interval_t *i = hw_param_interval_c(params, var);
if (interval_is_empty(i) || !interval_is_single(i))
return -EINVAL;
if (dir)
*dir = i->openmin;
if (val)
*val = interval_get_final(i);
return 0;
}
return -EINVAL;
}
int _snd_pcm_hw_param_get_min(const snd_pcm_hw_params_t *params,
int var, unsigned int *val, int *dir)
{
if (hw_is_mask(var)) {
const snd_mask_t *m = hw_param_mask_c(params, var);
if (dir)
*dir = 0;
if (val)
*val = mask_get_min(m);
return 0;
} else if (hw_is_interval(var)) {
const snd_interval_t *i = hw_param_interval_c(params, var);
if (dir)
*dir = i->openmin;
if (val)
*val = interval_get_min(i);
return 0;
}
return 0;
}
int _snd_pcm_hw_param_get_max(const snd_pcm_hw_params_t *params,
int var, unsigned int *val, int *dir)
{
if (hw_is_mask(var)) {
const snd_mask_t *m = hw_param_mask_c(params, var);
if (dir)
*dir = 0;
if (val)
*val = mask_get_max(m);
return 0;
} else if (hw_is_interval(var)) {
const snd_interval_t *i = hw_param_interval_c(params, var);
if (dir)
*dir = -(int)i->openmax;
if (val)
*val = interval_get_max(i);
return 0;
}
return 0;
}
static int hw_param_set_min(snd_pcm_hw_params_t *params,
int var, unsigned int val, int dir)
{
int openmin;
if (hw_is_mask(var))
return snd_mask_refine_min(hw_param_mask(params, var), val);
openmin = 0;
if (dir > 0)
openmin = 1;
else if (dir < 0 && val > 0) {
openmin = 1;
val--;
}
return snd_interval_refine_min(hw_param_interval(params, var),
val, openmin, 0);
}
static int hw_param_update_var(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var, int changed)
{
if (changed < 0)
return changed;
if (changed) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
if (params->rmask) {
changed = snd_pcm_hw_refine(pcm, params);
if (changed < 0)
return changed;
if (snd_pcm_hw_param_empty(params, var))
return -ENOENT;
}
return 0;
}
int _snd_pcm_hw_param_set_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var, unsigned int *val, int *dir)
{
snd_pcm_hw_params_t save;
int err;
save = *params;
err = hw_param_set_min(params, var, *val, dir ? *dir : 0);
err = hw_param_update_var(pcm, params, var, err);
if (!err)
return _snd_pcm_hw_param_get_min(params, var, val, dir);
*params = save;
return err;
}
static int hw_param_set_max(snd_pcm_hw_params_t *params,
int var, unsigned int val, int dir)
{
int openmax;
if (hw_is_mask(var))
return snd_mask_refine_max(hw_param_mask(params, var), val);
openmax = 0;
if (dir) {
openmax = 1;
if (dir > 0)
val++;
}
return snd_interval_refine_max(hw_param_interval(params, var),
val, openmax, 0);
}
int _snd_pcm_hw_param_set_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var, unsigned int *val, int *dir)
{
snd_pcm_hw_params_t save;
int err;
save = *params;
err = hw_param_set_max(params, var, *val, dir ? *dir : 0);
err = hw_param_update_var(pcm, params, var, err);
if (!err)
return _snd_pcm_hw_param_get_max(params, var, val, dir);
*params = save;
return err;
}
static int hw_param_set_minmax(snd_pcm_hw_params_t *params,
int var,
unsigned int min, int mindir,
unsigned int max, int maxdir)
{
int c1, c2;
c1 = hw_param_set_min(params, var, min, mindir);
if (c1 < 0)
return c1;
c2 = hw_param_set_max(params, var, max, maxdir);
if (c2 < 0)
return c2;
return c1 || c2;
}
int _snd_pcm_hw_param_set_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var,
unsigned int *min, int *mindir,
unsigned int *max, int *maxdir)
{
snd_pcm_hw_params_t save;
int err;
save = *params;
err = hw_param_set_minmax(params, var,
*min, mindir ? *mindir : 0,
*max, maxdir ? *maxdir : 0);
err = hw_param_update_var(pcm, params, var, err);
if (!err) {
err = _snd_pcm_hw_param_get_min(params, var, min, mindir);
if (err >= 0)
return _snd_pcm_hw_param_get_max(params, var, max,
maxdir);
}
*params = save;
return err;
}
static int hw_param_set(snd_pcm_hw_params_t *params,
int var, unsigned int val, int dir)
{
if (hw_is_mask(var)) {
snd_mask_t *m = hw_param_mask(params, var);
if (val == 0 && dir < 0) {
mask_clear(m);
return -EINVAL;
}
if (dir > 0)
val++;
else if (dir < 0)
val--;
return snd_mask_refine_set(hw_param_mask(params, var), val);
} else {
snd_interval_t *i = hw_param_interval(params, var);
if (val == 0 && dir < 0) {
i->empty = 1;
return -EINVAL;
}
if (dir == 0)
return snd_interval_refine_set(i, val);
else {
snd_interval_t t;
t.openmin = 1;
t.openmax = 1;
t.empty = 0;
t.integer = 0;
if (dir < 0) {
t.min = val - 1;
t.max = val;
} else {
t.min = val;
t.max = val + 1;
}
return snd_interval_refine(i, &t);
}
}
}
int _snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var, unsigned int val, int dir)
{
snd_pcm_hw_params_t save = *params;
int err = hw_param_set(params, var, val, dir);
err = hw_param_update_var(pcm, params, var, err);
if (err < 0)
*params = save;
return err;
}
int _snd_pcm_hw_param_test(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var, unsigned int val, int *dir)
{
snd_pcm_hw_params_t save = *params;
int err = _snd_pcm_hw_param_set(pcm, params, var, val, dir ? *dir : 0);
*params = save;
return err;
}
int _snd_pcm_hw_param_set_integer(snd_pcm_t *pcm,
snd_pcm_hw_params_t *params,
int var)
{
snd_interval_t *i = hw_param_interval(params, var);
snd_pcm_hw_params_t save;
int err;
if (i->integer)
return 0;
if (interval_check_empty(i))
return -EINVAL;
save = *params;
i->integer = 1;
err = hw_param_update_var(pcm, params, var, 1);
if (err < 0)
*params = save;
return err;
}
int _snd_pcm_hw_param_set_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var, const snd_mask_t *val)
{
if (snd_mask_refine(hw_param_mask(params, var), val)) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
if (params->rmask)
return snd_pcm_hw_refine(pcm, params);
return 0;
}
static int hw_param_set_first(snd_pcm_hw_params_t *params, int var)
{
if (hw_is_mask(var))
return snd_mask_refine_first(hw_param_mask(params, var));
else
return snd_interval_refine_first(hw_param_interval(params, var));
}
static int hw_param_set_last(snd_pcm_hw_params_t *params, int var)
{
if (hw_is_mask(var))
return snd_mask_refine_last(hw_param_mask(params, var));
else
return snd_interval_refine_last(hw_param_interval(params, var));
}
int _snd_pcm_hw_param_set_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var, unsigned int *val, int *dir)
{
int err = hw_param_set_first(params, var);
err = hw_param_update_var(pcm, params, var, err);
if (!err)
return _snd_pcm_hw_param_get(params, var, val, dir);
return err;
}
int _snd_pcm_hw_param_set_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var, unsigned int *val, int *dir)
{
int err = hw_param_set_last(params, var);
err = hw_param_update_var(pcm, params, var, err);
if (!err)
return _snd_pcm_hw_param_get(params, var, val, dir);
return err;
}
/*
*/
static void boundary_sub(int a, int adir, int b, int bdir, int *c, int *cdir)
{
adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0);
bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0);
a -= b;
*cdir = adir - bdir;
if (*cdir == -2)
a--;
else if (*cdir == 2)
a++;
*c = a;
}
static int boundary_lt(unsigned int a, int adir, unsigned int b, int bdir)
{
if (adir < 0) {
a--;
adir = 1;
} else if (adir > 0)
adir = 1;
if (bdir < 0) {
b--;
bdir = 1;
} else if (bdir > 0)
bdir = 1;
return a < b || (a == b && adir < bdir);
}
/* Return 1 if min is nearer to best than max */
static int boundary_nearer(int min, int mindir, int best, int bestdir,
int max, int maxdir)
{
int dmin, dmindir;
int dmax, dmaxdir;
boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir);
boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir);
return boundary_lt(dmin, dmindir, dmax, dmaxdir);
}
int _snd_pcm_hw_param_set_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
int var, unsigned int *val, int *dir)
{
snd_pcm_hw_params_t save;
int err;
unsigned int best = *val, saved_min;
int last = 0;
unsigned int min, max;
int mindir, maxdir;
int valdir = dir ? *dir : 0;
snd_interval_t *i;
if (best > INT_MAX)
best = INT_MAX;
min = max = best;
mindir = valdir;
if (valdir > 0)
maxdir = 0;
else if (valdir == 0)
maxdir = -1;
else {
maxdir = 1;
max--;
}
save = *params;
saved_min = min;
err = _snd_pcm_hw_param_set_min(pcm, params, var, &min, &mindir);
i = hw_param_interval(params, var);
if (!interval_is_empty(i) && interval_is_single(i))
return _snd_pcm_hw_param_get_min(params, var, val, dir);
if (err >= 0) {
snd_pcm_hw_params_t params1;
if (min == saved_min && mindir == valdir)
goto _end;
params1 = save;
err = _snd_pcm_hw_param_set_max(pcm, &params1, var,
&max, &maxdir);
if (err < 0)
goto _end;
if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
*params = params1;
last = 1;
}
} else {
*params = save;
err = _snd_pcm_hw_param_set_max(pcm, params, var, &max, &maxdir);
if (err < 0)
return err;
last = 1;
}
_end:
if (last)
err = _snd_pcm_hw_param_set_last(pcm, params, var, val, dir);
else
err = _snd_pcm_hw_param_set_first(pcm, params, var, val, dir);
return err;
}
snd_mask_t *_snd_pcm_hw_param_get_mask(snd_pcm_hw_params_t *params, int var)
{
return hw_param_mask(params, var);
}
static int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
struct param_choice {
char var;
char last;
};
static struct param_choice vars[] = {
{ SNDRV_PCM_HW_PARAM_ACCESS, 0 },
{ SNDRV_PCM_HW_PARAM_FORMAT, 0 },
{ SNDRV_PCM_HW_PARAM_SUBFORMAT, 0 },
{ SNDRV_PCM_HW_PARAM_CHANNELS, 0 },
{ SNDRV_PCM_HW_PARAM_RATE, 0 },
{ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 1 },
{ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0 },
{ SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0 },
{ SNDRV_PCM_HW_PARAM_TICK_TIME, 0 },
};
int i, err;
for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) {
if (vars[i].last)
err = _snd_pcm_hw_param_set_last(pcm, params,
vars[i].var,
NULL, NULL);
else
err = _snd_pcm_hw_param_set_first(pcm, params,
vars[i].var,
NULL, NULL);
if (err < 0)
return err;
}
return 0;
}
static int _snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
int err;
snd_pcm_sw_params_t sw;
err = snd_pcm_hw_refine(pcm, params);
if (err < 0)
return err;
snd_pcm_hw_params_choose(pcm, params);
snd_pcm_hw_free(pcm);
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params) < 0)
return -errno;
#if 0
params->info &= ~0xf0000000;
params->info |= (pcm->monotonic ? SND_PCM_INFO_MONOTONIC : 0);
#endif
pcm->setup = 1;
pcm->hw_params = *params;
snd_pcm_hw_params_get_access(params, &pcm->_access);
snd_pcm_hw_params_get_format(params, &pcm->format);
snd_pcm_hw_params_get_subformat(params, &pcm->subformat);
snd_pcm_hw_params_get_channels(params, &pcm->channels);
snd_pcm_hw_params_get_rate(params, &pcm->rate, 0);
snd_pcm_hw_params_get_period_time(params, &pcm->period_time, 0);
snd_pcm_hw_params_get_period_size(params, &pcm->period_size, 0);
snd_pcm_hw_params_get_buffer_size(params, &pcm->buffer_size);
pcm->sample_bits = snd_pcm_format_physical_width(pcm->format);
pcm->frame_bits = pcm->sample_bits * pcm->channels;
/* Default sw params */
memset(&sw, 0, sizeof(sw));
snd_pcm_sw_params_default(pcm, &sw);
snd_pcm_sw_params(pcm, &sw);
if (pcm->_access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
pcm->_access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED ||
pcm->_access == SND_PCM_ACCESS_MMAP_COMPLEX) {
err = _snd_pcm_mmap(pcm);
if (err < 0) {
_snd_pcm_munmap(pcm);
return err;
}
}
_snd_pcm_sync_ptr(pcm, 0);
return 0;
}
int snd_pcm_hw_params_get_min_align(const snd_pcm_hw_params_t *params,
snd_pcm_uframes_t *val)
{
unsigned int format, channels, fb, min_align;
int err;
err = _snd_pcm_hw_param_get(params, SNDRV_PCM_HW_PARAM_FORMAT,
&format, NULL);
if (err < 0)
return err;
err = _snd_pcm_hw_param_get(params, SNDRV_PCM_HW_PARAM_CHANNELS,
&channels, NULL);
if (err < 0)
return err;
/* compute frame bits */
fb = snd_pcm_format_physical_width(format) * channels;
min_align = 1;
while (fb % 8) {
fb *= 2;
min_align *= 2;
}
if (val)
*val = min_align;
return 0;
}
int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
int err;
err = _snd_pcm_hw_params(pcm, params);
if (err < 0)
return err;
err = snd_pcm_prepare(pcm);
return err;
}
int snd_pcm_hw_free(snd_pcm_t *pcm)
{
if (!pcm->setup)
return 0;
_snd_pcm_munmap(pcm);
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_FREE) < 0)
return -errno;
pcm->setup = 0;
return 0;
}
int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
if (!pcm->setup)
return -EBADFD;
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, params) < 0)
return -errno;
pcm->sw_params = *params;
pcm->mmap_control->avail_min = params->avail_min;
return 0;
}
/*
* DUMP HW PARAMS
*/
static void snd_mask_print(const snd_mask_t *mask, int var, snd_output_t *out)
{
unsigned int i;
if (mask_is_empty(mask)) {
snd_output_puts(out, " NONE");
return;
} else if (mask_is_full(mask)) {
snd_output_puts(out, " ALL");
return;
}
for (i = 0; i < SND_MASK_MAX; i++) {
const char *s;
if (!mask_get(mask, i))
continue;
switch (var) {
case SNDRV_PCM_HW_PARAM_ACCESS:
s = snd_pcm_access_name(i);
break;
case SNDRV_PCM_HW_PARAM_FORMAT:
s = snd_pcm_format_name(i);
break;
case SNDRV_PCM_HW_PARAM_SUBFORMAT:
s = snd_pcm_subformat_name(i);
break;
default:
continue;
}
snd_output_printf(out, " %s", s);
}
}
static void snd_interval_print(const snd_interval_t *i, snd_output_t *out)
{
if (interval_is_empty(i))
snd_output_printf(out, "NONE");
else if (interval_is_full(i))
snd_output_printf(out, "ALL");
else if (interval_is_single(i) && i->integer)
snd_output_printf(out, "%u", interval_get_final(i));
else
snd_output_printf(out, "%c%u %u%c",
i->openmin ? '(' : '[',
i->min, i->max,
i->openmax ? ')' : ']');
}
void snd_pcm_hw_param_dump(const snd_pcm_hw_params_t *params,
int var, snd_output_t *out)
{
if (hw_is_mask(var))
snd_mask_print(hw_param_mask_c(params, var), var, out);
else if (hw_is_interval(var))
snd_interval_print(hw_param_interval_c(params, var), out);
}
#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
static const char * const snd_pcm_hw_param_names[] = {
HW_PARAM(ACCESS),
HW_PARAM(FORMAT),
HW_PARAM(SUBFORMAT),
HW_PARAM(SAMPLE_BITS),
HW_PARAM(FRAME_BITS),
HW_PARAM(CHANNELS),
HW_PARAM(RATE),
HW_PARAM(PERIOD_TIME),
HW_PARAM(PERIOD_SIZE),
HW_PARAM(PERIOD_BYTES),
HW_PARAM(PERIODS),
HW_PARAM(BUFFER_TIME),
HW_PARAM(BUFFER_SIZE),
HW_PARAM(BUFFER_BYTES),
HW_PARAM(TICK_TIME),
};
static void dump_one_param(snd_pcm_hw_params_t *params, unsigned int k,
snd_output_t *out)
{
snd_output_printf(out, "%s: ", snd_pcm_hw_param_names[k]);
snd_pcm_hw_param_dump(params, k, out);
snd_output_putc(out, '\n');
}
int snd_pcm_hw_params_dump(snd_pcm_hw_params_t *params, snd_output_t *out)
{
unsigned int k;
for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++)
dump_one_param(params, k, out);
for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
dump_one_param(params, k, out);
return 0;
}
int snd_pcm_sw_params_dump(snd_pcm_sw_params_t *params, snd_output_t *out)
{
snd_output_printf(out, "tstamp_mode: %s\n",
snd_pcm_tstamp_mode_name(params->tstamp_mode));
snd_output_printf(out, "period_step: %u\n",
params->period_step);
snd_output_printf(out, "sleep_min: %u\n",
params->sleep_min);
snd_output_printf(out, "avail_min: %lu\n",
params->avail_min);
snd_output_printf(out, "xfer_align: %lu\n",
params->xfer_align);
snd_output_printf(out, "silence_threshold: %lu\n",
params->silence_threshold);
snd_output_printf(out, "silence_size: %lu\n",
params->silence_size);
snd_output_printf(out, "boundary: %lu\n",
params->boundary);
return 0;
}