/** * snd_pcm_hw_param_value_min * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or NULL * * Return the minimum value for field PAR.
*/ staticunsignedint
snd_pcm_hw_param_value_min(conststruct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
{ if (hw_is_mask(var)) { if (dir)
*dir = 0; return snd_mask_min(hw_param_mask_c(params, var));
} if (hw_is_interval(var)) { conststruct snd_interval *i = hw_param_interval_c(params, var); if (dir)
*dir = i->openmin; return snd_interval_min(i);
} return -EINVAL;
}
/** * snd_pcm_hw_param_value_max * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or NULL * * Return the maximum value for field PAR.
*/ staticint
snd_pcm_hw_param_value_max(conststruct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
{ if (hw_is_mask(var)) { if (dir)
*dir = 0; return snd_mask_max(hw_param_mask_c(params, var));
} if (hw_is_interval(var)) { conststruct snd_interval *i = hw_param_interval_c(params, var); if (dir)
*dir = - (int) i->openmax; return snd_interval_max(i);
} return -EINVAL;
}
staticint snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, conststruct snd_mask *val)
{ int changed = _snd_pcm_hw_param_mask(params, var, val); if (changed < 0) return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); if (err < 0) return err;
} return 0;
}
staticint _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, unsignedint val, int dir)
{ int changed; int open = 0; if (dir) { if (dir > 0) {
open = 1;
} elseif (dir < 0) { if (val > 0) {
open = 1;
val--;
}
}
} if (hw_is_mask(var))
changed = snd_mask_refine_min(hw_param_mask(params, var),
val + !!open); elseif (hw_is_interval(var))
changed = snd_interval_refine_min(hw_param_interval(params, var),
val, open); else return -EINVAL; if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
} return changed;
}
/** * snd_pcm_hw_param_min * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @val: minimal value * @dir: pointer to the direction (-1,0,1) or NULL * * Inside configuration space defined by PARAMS remove from PAR all * values < VAL. Reduce configuration space accordingly. * Return new minimum or -EINVAL if the configuration space is empty
*/ staticint snd_pcm_hw_param_min(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, unsignedint val, int *dir)
{ int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0); if (changed < 0) return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); if (err < 0) return err;
} return snd_pcm_hw_param_value_min(params, var, dir);
}
staticint _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, unsignedint val, int dir)
{ int changed; int open = 0; if (dir) { if (dir < 0) {
open = 1;
} elseif (dir > 0) {
open = 1;
val++;
}
} if (hw_is_mask(var)) { if (val == 0 && open) {
snd_mask_none(hw_param_mask(params, var));
changed = -EINVAL;
} else
changed = snd_mask_refine_max(hw_param_mask(params, var),
val - !!open);
} elseif (hw_is_interval(var))
changed = snd_interval_refine_max(hw_param_interval(params, var),
val, open); else return -EINVAL; if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
} return changed;
}
/** * snd_pcm_hw_param_max * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @val: maximal value * @dir: pointer to the direction (-1,0,1) or NULL * * Inside configuration space defined by PARAMS remove from PAR all * values >= VAL + 1. Reduce configuration space accordingly. * Return new maximum or -EINVAL if the configuration space is empty
*/ staticint snd_pcm_hw_param_max(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, unsignedint val, int *dir)
{ int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0); if (changed < 0) return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); if (err < 0) return err;
} return snd_pcm_hw_param_value_max(params, var, dir);
}
staticint 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);
*c = a - b;
*cdir = adir - bdir; if (*cdir == -2) {
(*c)--;
} elseif (*cdir == 2) {
(*c)++;
} return 0;
}
staticint boundary_lt(unsignedint a, int adir, unsignedint b, int bdir)
{ if (adir < 0) {
a--;
adir = 1;
} elseif (adir > 0)
adir = 1; if (bdir < 0) {
b--;
bdir = 1;
} elseif (bdir > 0)
bdir = 1; return a < b || (a == b && adir < bdir);
}
/* Return 1 if min is nearer to best than max */ staticint 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);
}
/** * snd_pcm_hw_param_near * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @best: value to set * @dir: pointer to the direction (-1,0,1) or NULL * * Inside configuration space defined by PARAMS set PAR to the available value * nearest to VAL. Reduce configuration space accordingly. * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. * Return the value found.
*/ staticint snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, unsignedint best, int *dir)
{ struct snd_pcm_hw_params *save __free(kfree) = NULL; int v; unsignedint saved_min; int last = 0; int min, max; int mindir, maxdir; int valdir = dir ? *dir : 0; /* FIXME */ if (best > INT_MAX)
best = INT_MAX;
min = max = best;
mindir = maxdir = valdir; if (maxdir > 0)
maxdir = 0; elseif (maxdir == 0)
maxdir = -1; else {
maxdir = 1;
max--;
}
save = kmalloc(sizeof(*save), GFP_KERNEL); if (save == NULL) return -ENOMEM;
*save = *params;
saved_min = min;
min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); if (min >= 0) { struct snd_pcm_hw_params *params1 __free(kfree) = NULL; if (max < 0) goto _end; if ((unsignedint)min == saved_min && mindir == valdir) goto _end;
params1 = kmalloc(sizeof(*params1), GFP_KERNEL); if (params1 == NULL) return -ENOMEM;
*params1 = *save;
max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir); if (max < 0) goto _end; if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
*params = *params1;
last = 1;
}
} else {
*params = *save;
max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); if (max < 0) return max;
last = 1;
}
_end: if (last)
v = snd_pcm_hw_param_last(pcm, params, var, dir); else
v = snd_pcm_hw_param_first(pcm, params, var, dir); return v;
}
/** * snd_pcm_hw_param_set * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @val: value to set * @dir: pointer to the direction (-1,0,1) or NULL * * Inside configuration space defined by PARAMS remove from PAR all * values != VAL. Reduce configuration space accordingly. * Return VAL or -EINVAL if the configuration space is empty
*/ staticint snd_pcm_hw_param_set(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, unsignedint val, int dir)
{ int changed = _snd_pcm_hw_param_set(params, var, val, dir); if (changed < 0) return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); if (err < 0) return err;
} return snd_pcm_hw_param_value(params, var, NULL);
}
static snd_pcm_format_t snd_pcm_oss_format_from(int format)
{ switch (format) { case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW; case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM; case AFMT_U8: return SNDRV_PCM_FORMAT_U8; case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE; case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE; case AFMT_S8: return SNDRV_PCM_FORMAT_S8; case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE; case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE; case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG; case AFMT_S32_LE: return SNDRV_PCM_FORMAT_S32_LE; case AFMT_S32_BE: return SNDRV_PCM_FORMAT_S32_BE; case AFMT_S24_LE: return SNDRV_PCM_FORMAT_S24_LE; case AFMT_S24_BE: return SNDRV_PCM_FORMAT_S24_BE; case AFMT_S24_PACKED: return SNDRV_PCM_FORMAT_S24_3LE; case AFMT_FLOAT: return SNDRV_PCM_FORMAT_FLOAT; case AFMT_SPDIF_RAW: return SNDRV_PCM_FORMAT_IEC958_SUBFRAME; default: return SNDRV_PCM_FORMAT_U8;
}
}
staticint snd_pcm_oss_format_to(snd_pcm_format_t format)
{ switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW; case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM; case SNDRV_PCM_FORMAT_U8: return AFMT_U8; case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE; case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE; case SNDRV_PCM_FORMAT_S8: return AFMT_S8; case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE; case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE; case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG; case SNDRV_PCM_FORMAT_S32_LE: return AFMT_S32_LE; case SNDRV_PCM_FORMAT_S32_BE: return AFMT_S32_BE; case SNDRV_PCM_FORMAT_S24_LE: return AFMT_S24_LE; case SNDRV_PCM_FORMAT_S24_BE: return AFMT_S24_BE; case SNDRV_PCM_FORMAT_S24_3LE: return AFMT_S24_PACKED; case SNDRV_PCM_FORMAT_FLOAT: return AFMT_FLOAT; case SNDRV_PCM_FORMAT_IEC958_SUBFRAME: return AFMT_SPDIF_RAW; default: return -EINVAL;
}
}
if (substream->oss.setup.periods > 1)
oss_periods = substream->oss.setup.periods;
s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL); if (s > 0 && runtime->oss.maxfrags && s > runtime->oss.maxfrags)
s = runtime->oss.maxfrags; if (oss_periods > s)
oss_periods = s;
s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL); if (s < 2)
s = 2; if (oss_periods < s)
oss_periods = s;
while (oss_period_size * oss_periods > oss_buffer_size)
oss_period_size /= 2;
if (oss_period_size < 16) return -EINVAL;
/* don't allocate too large period; 1MB period must be enough */ if (oss_period_size > 1024 * 1024) return -ENOMEM;
/* this one takes the lock by itself */ staticint snd_pcm_oss_change_params(struct snd_pcm_substream *substream, bool trylock)
{ struct snd_pcm_runtime *runtime = substream->runtime; int err;
if (trylock) { if (!(mutex_trylock(&runtime->oss.params_lock))) return -EAGAIN;
} elseif (mutex_lock_interruptible(&runtime->oss.params_lock)) return -ERESTARTSYS;
for (idx = 0; idx < 2; idx++) {
substream = pcm_oss_file->streams[idx]; if (substream == NULL) continue; if (asubstream == NULL)
asubstream = substream; if (substream->runtime->oss.params) {
err = snd_pcm_oss_change_params(substream, false); if (err < 0) return err;
}
} if (!asubstream) return -EIO; if (r_substream)
*r_substream = asubstream; return 0;
}
/* call with params_lock held */ /* NOTE: this always call PREPARE unconditionally no matter whether * runtime->oss.prepare is set or not
*/ staticint snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
{ int err; struct snd_pcm_runtime *runtime = substream->runtime;
while (1) {
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, delay); if (err < 0) break;
runtime = substream->runtime; if (*delay <= (snd_pcm_sframes_t)runtime->buffer_size) break; /* in case of overrun, skip whole periods like OSS/Linux driver does */ /* until avail(delay) <= buffer_size */
frames = (*delay - runtime->buffer_size) + runtime->period_size - 1;
frames /= runtime->period_size;
frames *= runtime->period_size;
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_FORWARD, &frames); if (err < 0) break;
} return err;
}
snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, constchar *ptr, snd_pcm_uframes_t frames, int in_kernel)
{ struct snd_pcm_runtime *runtime = substream->runtime; int ret; while (1) { if (runtime->state == SNDRV_PCM_STATE_XRUN ||
runtime->state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG
pcm_dbg(substream->pcm, "pcm_oss: write: recovering from %s\n",
runtime->state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif
ret = snd_pcm_oss_prepare(substream); if (ret < 0) break;
}
mutex_unlock(&runtime->oss.params_lock);
ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
frames, in_kernel);
mutex_lock(&runtime->oss.params_lock); if (ret != -EPIPE && ret != -ESTRPIPE) break; /* test, if we can't store new data, because the stream */ /* has not been started */ if (runtime->state == SNDRV_PCM_STATE_PREPARED) return -EAGAIN;
} return ret;
}
snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel)
{ struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t delay; int ret; while (1) { if (runtime->state == SNDRV_PCM_STATE_XRUN ||
runtime->state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG
pcm_dbg(substream->pcm, "pcm_oss: read: recovering from %s\n",
runtime->state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); if (ret < 0) break;
} elseif (runtime->state == SNDRV_PCM_STATE_SETUP) {
ret = snd_pcm_oss_prepare(substream); if (ret < 0) break;
}
ret = snd_pcm_oss_capture_position_fixup(substream, &delay); if (ret < 0) break;
mutex_unlock(&runtime->oss.params_lock);
ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
frames, in_kernel);
mutex_lock(&runtime->oss.params_lock); if (ret == -EPIPE) { if (runtime->state == SNDRV_PCM_STATE_DRAINING) {
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); if (ret < 0) break;
} continue;
} if (ret != -ESTRPIPE) break;
} return ret;
}
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
{ struct snd_pcm_runtime *runtime = substream->runtime; int ret; while (1) { if (runtime->state == SNDRV_PCM_STATE_XRUN ||
runtime->state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG
pcm_dbg(substream->pcm, "pcm_oss: writev: recovering from %s\n",
runtime->state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif
ret = snd_pcm_oss_prepare(substream); if (ret < 0) break;
}
ret = snd_pcm_kernel_writev(substream, bufs, frames); if (ret != -EPIPE && ret != -ESTRPIPE) break;
/* test, if we can't store new data, because the stream */ /* has not been started */ if (runtime->state == SNDRV_PCM_STATE_PREPARED) return -EAGAIN;
} return ret;
}
snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
{ struct snd_pcm_runtime *runtime = substream->runtime; int ret; while (1) { if (runtime->state == SNDRV_PCM_STATE_XRUN ||
runtime->state == SNDRV_PCM_STATE_SUSPENDED) { #ifdef OSS_DEBUG
pcm_dbg(substream->pcm, "pcm_oss: readv: recovering from %s\n",
runtime->state == SNDRV_PCM_STATE_XRUN ? "XRUN" : "SUSPEND"); #endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); if (ret < 0) break;
} elseif (runtime->state == SNDRV_PCM_STATE_SETUP) {
ret = snd_pcm_oss_prepare(substream); if (ret < 0) break;
}
ret = snd_pcm_kernel_readv(substream, bufs, frames); if (ret != -EPIPE && ret != -ESTRPIPE) break;
} return ret;
} #endif/* CONFIG_SND_PCM_OSS_PLUGINS */
for (i = 0; i < 2; i++) {
substream = pcm_oss_file->streams[i]; if (!substream) continue;
runtime = substream->runtime;
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
mutex_lock(&runtime->oss.params_lock);
runtime->oss.prepare = 1;
runtime->oss.buffer_used = 0;
runtime->oss.prev_hw_ptr_period = 0;
runtime->oss.period_ptr = 0;
mutex_unlock(&runtime->oss.params_lock);
} return 0;
}
staticint snd_pcm_oss_post(struct snd_pcm_oss_file *pcm_oss_file)
{ struct snd_pcm_substream *substream; int err;
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; if (substream != NULL) {
err = snd_pcm_oss_make_ready(substream); if (err < 0) return err;
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
} /* note: all errors from the start action are ignored */ /* OSS apps do not know, how to handle them */ return 0;
}
staticint snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
{ struct snd_pcm_runtime *runtime;
ssize_t result = 0;
snd_pcm_state_t state; long res;
wait_queue_entry_t wait;
runtime = substream->runtime;
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait); #ifdef OSS_DEBUG
pcm_dbg(substream->pcm, "sync1: size = %li\n", size); #endif while (1) {
result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1); if (result > 0) {
runtime->oss.buffer_used = 0;
result = 0; break;
} if (result != 0 && result != -EAGAIN) break;
result = 0;
set_current_state(TASK_INTERRUPTIBLE);
scoped_guard(pcm_stream_lock_irq, substream)
state = runtime->state; if (state != SNDRV_PCM_STATE_RUNNING) {
set_current_state(TASK_RUNNING); break;
}
res = schedule_timeout(10 * HZ); if (signal_pending(current)) {
result = -ERESTARTSYS; break;
} if (res == 0) {
pcm_err(substream->pcm, "OSS sync error - DMA timeout\n");
result = -EIO; break;
}
}
remove_wait_queue(&runtime->sleep, &wait); return result;
}
staticint snd_pcm_oss_get_caps1(struct snd_pcm_substream *substream, int res)
{
if (substream == NULL) {
res &= ~DSP_CAP_DUPLEX; return res;
} #ifdef DSP_CAP_MULTI if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->pstr->substream_count > 1)
res |= DSP_CAP_MULTI; #endif /* DSP_CAP_REALTIME is set all times: */ /* all ALSA drivers can return actual pointer in ring buffer */ #ifdefined(DSP_CAP_REALTIME) && 0
{ struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH))
res &= ~DSP_CAP_REALTIME;
} #endif return res;
}
staticint snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file)
{ int result, idx;
result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME; for (idx = 0; idx < 2; idx++) { struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
result = snd_pcm_oss_get_caps1(substream, result);
}
result |= 0x0001; /* revision - same as SB AWE 64 */ return result;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.