/* * fill ring buffer with silence * runtime->silence_start: starting pointer to silence area * runtime->silence_filled: size filled with silence * runtime->silence_threshold: threshold from application * runtime->silence_size: maximal size from application * * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately
*/ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr)
{ struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t frames, ofs, transfer; int err;
if (runtime->silence_size < runtime->boundary) {
snd_pcm_sframes_t noise_dist;
snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr);
update_silence_vars(runtime, runtime->silence_start, appl_ptr); /* initialization outside pointer updates */ if (new_hw_ptr == ULONG_MAX)
new_hw_ptr = runtime->status->hw_ptr; /* get hw_avail with the boundary crossing */
noise_dist = appl_ptr - new_hw_ptr; if (noise_dist < 0)
noise_dist += runtime->boundary; /* total noise distance */
noise_dist += runtime->silence_filled; if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) return;
frames = runtime->silence_threshold - noise_dist; if (frames > runtime->silence_size)
frames = runtime->silence_size;
} else { /* * This filling mode aims at free-running mode (used for example by dmix), * which doesn't update the application pointer.
*/
snd_pcm_uframes_t hw_ptr = runtime->status->hw_ptr; if (new_hw_ptr == ULONG_MAX) { /* * Initialization, fill the whole unused buffer with silence. * * Usually, this is entered while stopped, before data is queued, * so both pointers are expected to be zero.
*/
snd_pcm_sframes_t avail = runtime->control->appl_ptr - hw_ptr; if (avail < 0)
avail += runtime->boundary; /* * In free-running mode, appl_ptr will be zero even while running, * so we end up with a huge number. There is no useful way to * handle this, so we just clear the whole buffer.
*/
runtime->silence_filled = avail > runtime->buffer_size ? 0 : avail;
runtime->silence_start = hw_ptr;
} else { /* Silence the just played area immediately */
update_silence_vars(runtime, hw_ptr, new_hw_ptr);
} /* * In this mode, silence_filled actually includes the valid * sample data from the user.
*/
frames = runtime->buffer_size - runtime->silence_filled;
} if (snd_BUG_ON(frames > runtime->buffer_size)) return; if (frames == 0) return;
ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size; do {
transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
err = fill_silence_frames(substream, ofs, transfer);
snd_BUG_ON(err < 0);
runtime->silence_filled += transfer;
frames -= transfer;
ofs = 0;
} while (frames > 0);
snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
}
/* * re-take a driver timestamp to let apps detect if the reference tstamp * read by low-level hardware was provided with a delay
*/
snd_pcm_gettime(substream->runtime, &driver_tstamp);
runtime->driver_tstamp = driver_tstamp;
}
/* * group pointer, time and jiffies reads to allow for more * accurate correlations/corrections. * The values are stored at the end of this routine after * corrections for hw_ptr position
*/
pos = substream->ops->pointer(substream);
curr_jiffies = jiffies; if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { if ((substream->ops->get_time_info) &&
(runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
substream->ops->get_time_info(substream, &curr_tstamp,
&audio_tstamp,
&runtime->audio_tstamp_config,
&runtime->audio_tstamp_report);
/* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)
snd_pcm_gettime(runtime, &curr_tstamp);
} else
snd_pcm_gettime(runtime, &curr_tstamp);
}
if (pos == SNDRV_PCM_POS_XRUN) {
__snd_pcm_xrun(substream); return -EPIPE;
} if (pos >= runtime->buffer_size) { if (printk_ratelimit()) { char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
pcm_err(substream->pcm, "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
name, pos, runtime->buffer_size,
runtime->period_size);
}
pos = 0;
}
pos -= pos % runtime->min_align;
trace_hwptr(substream, pos, in_interrupt);
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos; if (in_interrupt) { /* we know that one period was processed */ /* delta = "expected next hw_ptr" for in_interrupt != 0 */
delta = runtime->hw_ptr_interrupt + runtime->period_size; if (delta > new_hw_ptr) { /* check for double acknowledged interrupts */
hdelta = curr_jiffies - runtime->hw_ptr_jiffies; if (hdelta > runtime->hw_ptr_buffer_jiffies/2 + 1) {
hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) {
hw_base = 0;
crossed_boundary++;
}
new_hw_ptr = hw_base + pos; goto __delta;
}
}
} /* new_hw_ptr might be lower than old_hw_ptr in case when */ /* pointer crosses the end of the ring buffer */ if (new_hw_ptr < old_hw_ptr) {
hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) {
hw_base = 0;
crossed_boundary++;
}
new_hw_ptr = hw_base + pos;
}
__delta:
delta = new_hw_ptr - old_hw_ptr; if (delta < 0)
delta += runtime->boundary;
if (runtime->no_period_wakeup) {
snd_pcm_sframes_t xrun_threshold; /* * Without regular period interrupts, we have to check * the elapsed time to detect xruns.
*/
jdelta = curr_jiffies - runtime->hw_ptr_jiffies; if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) goto no_delta_check;
hdelta = jdelta - delta * HZ / runtime->rate;
xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1; while (hdelta > xrun_threshold) {
delta += runtime->buffer_size;
hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) {
hw_base = 0;
crossed_boundary++;
}
new_hw_ptr = hw_base + pos;
hdelta -= runtime->hw_ptr_buffer_jiffies;
} goto no_delta_check;
}
/* something must be really wrong */ if (delta >= runtime->buffer_size + runtime->period_size) {
hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr", "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
substream->stream, (long)pos,
(long)new_hw_ptr, (long)old_hw_ptr); return 0;
}
/* Do jiffies check only in xrun_debug mode */ if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) goto no_jiffies_check;
/* Skip the jiffies check for hardwares with BATCH flag. * Such hardware usually just increases the position at each IRQ, * thus it can't give any strange position.
*/ if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) goto no_jiffies_check;
hdelta = delta; if (hdelta < runtime->delay) goto no_jiffies_check;
hdelta -= runtime->delay;
jdelta = curr_jiffies - runtime->hw_ptr_jiffies; if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
delta = jdelta /
(((runtime->period_size * HZ) / runtime->rate)
+ HZ/100); /* move new_hw_ptr according jiffies not pos variable */
new_hw_ptr = old_hw_ptr;
hw_base = delta; /* use loop to avoid checks for delta overflows */ /* the delta value is small or zero in most cases */ while (delta > 0) {
new_hw_ptr += runtime->period_size; if (new_hw_ptr >= runtime->boundary) {
new_hw_ptr -= runtime->boundary;
crossed_boundary--;
}
delta--;
} /* align hw_base to buffer_size */
hw_ptr_error(substream, in_interrupt, "hw_ptr skipping", "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
(long)pos, (long)hdelta,
(long)runtime->period_size, jdelta,
((hdelta * HZ) / runtime->rate), hw_base,
(unsignedlong)old_hw_ptr,
(unsignedlong)new_hw_ptr); /* reset values to proper state */
delta = 0;
hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
}
no_jiffies_check: if (delta > runtime->period_size + runtime->period_size / 2) {
hw_ptr_error(substream, in_interrupt, "Lost interrupts?", "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
substream->stream, (long)delta,
(long)new_hw_ptr,
(long)old_hw_ptr);
}
/** * snd_pcm_set_sync_per_card - set the PCM sync id with card number * @substream: the pcm substream * @params: modified hardware parameters * @id: identifier (max 12 bytes) * @len: identifier length (max 12 bytes) * * Sets the PCM sync identifier for the card with zero padding. * * User space or any user should use this 16-byte identifier for a comparison only * to check if two IDs are similar or different. Special case is the identifier * containing only zeros. Interpretation for this combination is - empty (not set). * The contents of the identifier should not be interpreted in any other way. * * The synchronization ID must be unique per clock source (usually one sound card, * but multiple soundcard may use one PCM word clock source which means that they * are fully synchronized). * * This routine composes this ID using card number in first four bytes and * 12-byte additional ID. When other ID composition is used (e.g. for multiple * sound cards), make sure that the composition does not clash with this * composition scheme.
*/ void snd_pcm_set_sync_per_card(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, constunsignedchar *id, unsignedint len)
{
*(__u32 *)params->sync = cpu_to_le32(substream->pcm->card->number);
len = min(12, len);
memcpy(params->sync + 4, id, len);
memset(params->sync + 4 + len, 0, 12 - len);
}
EXPORT_SYMBOL_GPL(snd_pcm_set_sync_per_card);
/* * Standard ioctl routine
*/
staticinlineunsignedint div32(unsignedint a, unsignedint b, unsignedint *r)
{ if (b == 0) {
*r = 0; return UINT_MAX;
}
*r = a % b; return a / b;
}
staticinlineunsignedint div_down(unsignedint a, unsignedint b)
{ if (b == 0) return UINT_MAX; return a / b;
}
staticinlineunsignedint div_up(unsignedint a, unsignedint b)
{ unsignedint r; unsignedint q; if (b == 0) return UINT_MAX;
q = div32(a, b, &r); if (r)
++q; return q;
}
staticinlineunsignedint mul(unsignedint a, unsignedint b)
{ if (a == 0) return 0; if (div_down(UINT_MAX, a) < b) return UINT_MAX; return a * b;
}
staticinlineunsignedint muldiv32(unsignedint a, unsignedint b, unsignedint c, unsignedint *r)
{
u_int64_t n = (u_int64_t) a * b; if (c == 0) {
*r = 0; return UINT_MAX;
}
n = div_u64_rem(n, c, r); if (n >= UINT_MAX) {
*r = 0; return UINT_MAX;
} return n;
}
/** * snd_interval_refine - refine the interval value of configurator * @i: the interval value to refine * @v: the interval value to refer to * * Refines the interval value with the reference value. * The interval is changed to the range satisfying both intervals. * The interval status (min, max, integer, etc.) are evaluated. * * Return: Positive if the value is changed, zero if it's not changed, or a * negative error code.
*/ int snd_interval_refine(struct snd_interval *i, conststruct snd_interval *v)
{ int changed = 0; if (snd_BUG_ON(snd_interval_empty(i))) return -EINVAL; if (i->min < v->min) {
i->min = v->min;
i->openmin = v->openmin;
changed = 1;
} elseif (i->min == v->min && !i->openmin && v->openmin) {
i->openmin = 1;
changed = 1;
} if (i->max > v->max) {
i->max = v->max;
i->openmax = v->openmax;
changed = 1;
} elseif (i->max == v->max && !i->openmax && v->openmax) {
i->openmax = 1;
changed = 1;
} if (!i->integer && v->integer) {
i->integer = 1;
changed = 1;
} if (i->integer) { if (i->openmin) {
i->min++;
i->openmin = 0;
} if (i->openmax) {
i->max--;
i->openmax = 0;
}
} elseif (!i->openmin && !i->openmax && i->min == i->max)
i->integer = 1; if (snd_interval_checkempty(i)) {
snd_interval_none(i); return -EINVAL;
} return changed;
}
EXPORT_SYMBOL(snd_interval_refine);
if (snd_BUG_ON(snd_interval_empty(i))) return -EINVAL; if (snd_interval_single(i)) return 0;
i->max = i->min; if (i->openmin)
i->max++; /* only exclude max value if also excluded before refine */
i->openmax = (i->openmax && i->max >= last_max); return 1;
}
if (snd_BUG_ON(snd_interval_empty(i))) return -EINVAL; if (snd_interval_single(i)) return 0;
i->min = i->max; if (i->openmax)
i->min--; /* only exclude min value if also excluded before refine */
i->openmin = (i->openmin && i->min <= last_min); return 1;
}
/** * snd_interval_div - refine the interval value with division * @a: dividend * @b: divisor * @c: quotient * * c = a / b * * Returns non-zero if the value is changed, zero if not changed.
*/ void snd_interval_div(conststruct snd_interval *a, conststruct snd_interval *b, struct snd_interval *c)
{ unsignedint r; if (a->empty || b->empty) {
snd_interval_none(c); return;
}
c->empty = 0;
c->min = div32(a->min, b->max, &r);
c->openmin = (r || a->openmin || b->openmax); if (b->min > 0) {
c->max = div32(a->max, b->min, &r); if (r) {
c->max++;
c->openmax = 1;
} else
c->openmax = (a->openmax || b->openmin);
} else {
c->max = UINT_MAX;
c->openmax = 0;
}
c->integer = 0;
}
/** * snd_interval_muldivk - refine the interval value * @a: dividend 1 * @b: dividend 2 * @k: divisor (as integer) * @c: result * * c = a * b / k * * Returns non-zero if the value is changed, zero if not changed.
*/ void snd_interval_muldivk(conststruct snd_interval *a, conststruct snd_interval *b, unsignedint k, struct snd_interval *c)
{ unsignedint r; if (a->empty || b->empty) {
snd_interval_none(c); return;
}
c->empty = 0;
c->min = muldiv32(a->min, b->min, k, &r);
c->openmin = (r || a->openmin || b->openmin);
c->max = muldiv32(a->max, b->max, k, &r); if (r) {
c->max++;
c->openmax = 1;
} else
c->openmax = (a->openmax || b->openmax);
c->integer = 0;
}
/** * snd_interval_mulkdiv - refine the interval value * @a: dividend 1 * @k: dividend 2 (as integer) * @b: divisor * @c: result * * c = a * k / b * * Returns non-zero if the value is changed, zero if not changed.
*/ void snd_interval_mulkdiv(conststruct snd_interval *a, unsignedint k, conststruct snd_interval *b, struct snd_interval *c)
{ unsignedint r; if (a->empty || b->empty) {
snd_interval_none(c); return;
}
c->empty = 0;
c->min = muldiv32(a->min, k, b->max, &r);
c->openmin = (r || a->openmin || b->openmax); if (b->min > 0) {
c->max = muldiv32(a->max, k, b->min, &r); if (r) {
c->max++;
c->openmax = 1;
} else
c->openmax = (a->openmax || b->openmin);
} else {
c->max = UINT_MAX;
c->openmax = 0;
}
c->integer = 0;
}
/* ---- */
/** * snd_interval_ratnum - refine the interval value * @i: interval to refine * @rats_count: number of ratnum_t * @rats: ratnum_t array * @nump: pointer to store the resultant numerator * @denp: pointer to store the resultant denominator * * Return: Positive if the value is changed, zero if it's not changed, or a * negative error code.
*/ int snd_interval_ratnum(struct snd_interval *i, unsignedint rats_count, conststruct snd_ratnum *rats, unsignedint *nump, unsignedint *denp)
{ unsignedint best_num, best_den; int best_diff; unsignedint k; struct snd_interval t; int err; unsignedint result_num, result_den; int result_diff;
best_num = best_den = best_diff = 0; for (k = 0; k < rats_count; ++k) { unsignedint num = rats[k].num; unsignedint den; unsignedint q = i->min; int diff; if (q == 0)
q = 1;
den = div_up(num, q); if (den < rats[k].den_min) continue; if (den > rats[k].den_max)
den = rats[k].den_max; else { unsignedint r;
r = (den - rats[k].den_min) % rats[k].den_step; if (r != 0)
den -= r;
}
diff = num - q * den; if (diff < 0)
diff = -diff; if (best_num == 0 ||
diff * best_den < best_diff * den) {
best_diff = diff;
best_den = den;
best_num = num;
}
} if (best_den == 0) {
i->empty = 1; return -EINVAL;
}
t.min = div_down(best_num, best_den);
t.openmin = !!(best_num % best_den);
result_num = best_num;
result_diff = best_diff;
result_den = best_den;
best_num = best_den = best_diff = 0; for (k = 0; k < rats_count; ++k) { unsignedint num = rats[k].num; unsignedint den; unsignedint q = i->max; int diff; if (q == 0) {
i->empty = 1; return -EINVAL;
}
den = div_down(num, q); if (den > rats[k].den_max) continue; if (den < rats[k].den_min)
den = rats[k].den_min; else { unsignedint r;
r = (den - rats[k].den_min) % rats[k].den_step; if (r != 0)
den += rats[k].den_step - r;
}
diff = q * den - num; if (diff < 0)
diff = -diff; if (best_num == 0 ||
diff * best_den < best_diff * den) {
best_diff = diff;
best_den = den;
best_num = num;
}
} if (best_den == 0) {
i->empty = 1; return -EINVAL;
}
t.max = div_up(best_num, best_den);
t.openmax = !!(best_num % best_den);
t.integer = 0;
err = snd_interval_refine(i, &t); if (err < 0) return err;
if (snd_interval_single(i)) { if (best_diff * result_den < result_diff * best_den) {
result_num = best_num;
result_den = best_den;
} if (nump)
*nump = result_num; if (denp)
*denp = result_den;
} return err;
}
EXPORT_SYMBOL(snd_interval_ratnum);
/** * snd_interval_ratden - refine the interval value * @i: interval to refine * @rats_count: number of struct ratden * @rats: struct ratden array * @nump: pointer to store the resultant numerator * @denp: pointer to store the resultant denominator * * Return: Positive if the value is changed, zero if it's not changed, or a * negative error code.
*/ staticint snd_interval_ratden(struct snd_interval *i, unsignedint rats_count, conststruct snd_ratden *rats, unsignedint *nump, unsignedint *denp)
{ unsignedint best_num, best_diff, best_den; unsignedint k; struct snd_interval t; int err;
best_num = best_den = best_diff = 0; for (k = 0; k < rats_count; ++k) { unsignedint num; unsignedint den = rats[k].den; unsignedint q = i->min; int diff;
num = mul(q, den); if (num > rats[k].num_max) continue; if (num < rats[k].num_min)
num = rats[k].num_max; else { unsignedint r;
r = (num - rats[k].num_min) % rats[k].num_step; if (r != 0)
num += rats[k].num_step - r;
}
diff = num - q * den; if (best_num == 0 ||
diff * best_den < best_diff * den) {
best_diff = diff;
best_den = den;
best_num = num;
}
} if (best_den == 0) {
i->empty = 1; return -EINVAL;
}
t.min = div_down(best_num, best_den);
t.openmin = !!(best_num % best_den);
best_num = best_den = best_diff = 0; for (k = 0; k < rats_count; ++k) { unsignedint num; unsignedint den = rats[k].den; unsignedint q = i->max; int diff;
num = mul(q, den); if (num < rats[k].num_min) continue; if (num > rats[k].num_max)
num = rats[k].num_max; else { unsignedint r;
r = (num - rats[k].num_min) % rats[k].num_step; if (r != 0)
num -= r;
}
diff = q * den - num; if (best_num == 0 ||
diff * best_den < best_diff * den) {
best_diff = diff;
best_den = den;
best_num = num;
}
} if (best_den == 0) {
i->empty = 1; return -EINVAL;
}
t.max = div_up(best_num, best_den);
t.openmax = !!(best_num % best_den);
t.integer = 0;
err = snd_interval_refine(i, &t); if (err < 0) return err;
if (snd_interval_single(i)) { if (nump)
*nump = best_num; if (denp)
*denp = best_den;
} return err;
}
/** * snd_interval_list - refine the interval value from the list * @i: the interval value to refine * @count: the number of elements in the list * @list: the value list * @mask: the bit-mask to evaluate * * Refines the interval value from the list. * When mask is non-zero, only the elements corresponding to bit 1 are * evaluated. * * Return: Positive if the value is changed, zero if it's not changed, or a * negative error code.
*/ int snd_interval_list(struct snd_interval *i, unsignedint count, constunsignedint *list, unsignedint mask)
{ unsignedint k; struct snd_interval list_range;
/** * snd_interval_ranges - refine the interval value from the list of ranges * @i: the interval value to refine * @count: the number of elements in the list of ranges * @ranges: the ranges list * @mask: the bit-mask to evaluate * * Refines the interval value from the list of ranges. * When mask is non-zero, only the elements corresponding to bit 1 are * evaluated. * * Return: Positive if the value is changed, zero if it's not changed, or a * negative error code.
*/ int snd_interval_ranges(struct snd_interval *i, unsignedint count, conststruct snd_interval *ranges, unsignedint mask)
{ unsignedint k; struct snd_interval range_union; struct snd_interval range;
if (!count) {
snd_interval_none(i); return -EINVAL;
}
snd_interval_any(&range_union);
range_union.min = UINT_MAX;
range_union.max = 0; for (k = 0; k < count; k++) { if (mask && !(mask & (1 << k))) continue;
snd_interval_copy(&range, &ranges[k]); if (snd_interval_refine(&range, i) < 0) continue; if (snd_interval_empty(&range)) continue;
/** * snd_pcm_hw_rule_add - add the hw-constraint rule * @runtime: the pcm runtime instance * @cond: condition bits * @var: the variable to evaluate * @func: the evaluation function * @private: the private data pointer passed to function * @dep: the dependent variables * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsignedint cond, int var,
snd_pcm_hw_rule_func_t func, void *private, int dep, ...)
{ struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; struct snd_pcm_hw_rule *c; unsignedint k;
va_list args;
va_start(args, dep); if (constrs->rules_num >= constrs->rules_all) { struct snd_pcm_hw_rule *new; unsignedint new_rules = constrs->rules_all + 16; new = krealloc_array(constrs->rules, new_rules, sizeof(*c), GFP_KERNEL); if (!new) {
va_end(args); return -ENOMEM;
}
constrs->rules = new;
constrs->rules_all = new_rules;
}
c = &constrs->rules[constrs->rules_num];
c->cond = cond;
c->func = func;
c->var = var;
c->private = private;
k = 0; while (1) { if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) {
va_end(args); return -EINVAL;
}
c->deps[k++] = dep; if (dep < 0) break;
dep = va_arg(args, int);
}
constrs->rules_num++;
va_end(args); return 0;
}
EXPORT_SYMBOL(snd_pcm_hw_rule_add);
/** * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint * @runtime: PCM runtime instance * @var: hw_params variable to apply the mask * @mask: the bitmap mask * * Apply the constraint of the given bitmap mask to a 32-bit mask parameter. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int32_t mask)
{ struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; struct snd_mask *maskp = constrs_mask(constrs, var);
*maskp->bits &= mask;
memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ if (*maskp->bits == 0) return -EINVAL; return 0;
}
/** * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint * @runtime: PCM runtime instance * @var: hw_params variable to apply the mask * @mask: the 64bit bitmap mask * * Apply the constraint of the given bitmap mask to a 64-bit mask parameter. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int64_t mask)
{ struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; struct snd_mask *maskp = constrs_mask(constrs, var);
maskp->bits[0] &= (u_int32_t)mask;
maskp->bits[1] &= (u_int32_t)(mask >> 32);
memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ if (! maskp->bits[0] && ! maskp->bits[1]) return -EINVAL; return 0;
}
EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64);
/** * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval * @runtime: PCM runtime instance * @var: hw_params variable to apply the integer constraint * * Apply the constraint of integer to an interval parameter. * * Return: Positive if the value is changed, zero if it's not changed, or a * negative error code.
*/ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var)
{ struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; return snd_interval_setinteger(constrs_interval(constrs, var));
}
EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
/** * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval * @runtime: PCM runtime instance * @var: hw_params variable to apply the range * @min: the minimal value * @max: the maximal value * * Apply the min/max range constraint to an interval parameter. * * Return: Positive if the value is changed, zero if it's not changed, or a * negative error code.
*/ int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, unsignedint min, unsignedint max)
{ struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; struct snd_interval t;
t.min = min;
t.max = max;
t.openmin = t.openmax = 0;
t.integer = 0; return snd_interval_refine(constrs_interval(constrs, var), &t);
}
EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
/** * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the list constraint * @l: list * * Apply the list of constraints to an interval parameter. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsignedint cond,
snd_pcm_hw_param_t var, conststruct snd_pcm_hw_constraint_list *l)
{ return snd_pcm_hw_rule_add(runtime, cond, var,
snd_pcm_hw_rule_list, (void *)l,
var, -1);
}
EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
/** * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the list of range constraints * @r: ranges * * Apply the list of range constraints to an interval parameter. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, unsignedint cond,
snd_pcm_hw_param_t var, conststruct snd_pcm_hw_constraint_ranges *r)
{ return snd_pcm_hw_rule_add(runtime, cond, var,
snd_pcm_hw_rule_ranges, (void *)r,
var, -1);
}
EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges);
/** * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule * @runtime: PCM runtime instance * @cond: condition bits * @width: sample bits width * @msbits: msbits width * * This constraint will set the number of most significant bits (msbits) if a * sample format with the specified width has been select. If width is set to 0 * the msbits will be set for any sample format with a width larger than the * specified msbits. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, unsignedint cond, unsignedint width, unsignedint msbits)
{ unsignedlong l = (msbits << 16) | width; return snd_pcm_hw_rule_add(runtime, cond, -1,
snd_pcm_hw_rule_msbits,
(void*) l,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
}
EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
/** * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling * @runtime: PCM runtime instance * @base_rate: the rate at which the hardware does not resample * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime, unsignedint base_rate)
{ return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE,
SNDRV_PCM_HW_PARAM_RATE,
snd_pcm_hw_rule_noresample_func,
(void *)(uintptr_t)base_rate,
SNDRV_PCM_HW_PARAM_RATE, -1);
}
EXPORT_SYMBOL(snd_pcm_hw_rule_noresample);
void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params)
{ unsignedint 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->info = ~0U;
}
EXPORT_SYMBOL(_snd_pcm_hw_params_any);
/** * snd_pcm_hw_param_value - return @params field @var value * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or %NULL * * Return: The value for field @var if it's fixed in configuration space * defined by @params. -%EINVAL otherwise.
*/ int snd_pcm_hw_param_value(conststruct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
{ if (hw_is_mask(var)) { conststruct snd_mask *mask = hw_param_mask_c(params, var); if (!snd_mask_single(mask)) return -EINVAL; if (dir)
*dir = 0; return snd_mask_value(mask);
} if (hw_is_interval(var)) { conststruct snd_interval *i = hw_param_interval_c(params, var); if (!snd_interval_single(i)) return -EINVAL; if (dir)
*dir = i->openmin; return snd_interval_value(i);
} return -EINVAL;
}
EXPORT_SYMBOL(snd_pcm_hw_param_value);
/** * snd_pcm_hw_param_first - refine config space and return minimum value * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or %NULL * * Inside configuration space defined by @params remove from @var all * values > minimum. Reduce configuration space accordingly. * * Return: The minimum, or a negative error code on failure.
*/ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
{ int changed = _snd_pcm_hw_param_first(params, var); 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, dir);
}
EXPORT_SYMBOL(snd_pcm_hw_param_first);
/** * snd_pcm_hw_param_last - refine config space and return maximum value * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or %NULL * * Inside configuration space defined by @params remove from @var all * values < maximum. Reduce configuration space accordingly. * * Return: The maximum, or a negative error code on failure.
*/ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
{ int changed = _snd_pcm_hw_param_last(params, var); 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, dir);
}
EXPORT_SYMBOL(snd_pcm_hw_param_last);
/** * snd_pcm_hw_params_bits - Get the number of bits per the sample. * @p: hardware parameters * * Return: The number of bits per sample based on the format, * subformat and msbits the specified hw params has.
*/ int snd_pcm_hw_params_bits(conststruct snd_pcm_hw_params *p)
{
snd_pcm_subformat_t subformat = params_subformat(p);
snd_pcm_format_t format = params_format(p);
switch (format) { case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_U32_LE: case SNDRV_PCM_FORMAT_S32_BE: case SNDRV_PCM_FORMAT_U32_BE: switch (subformat) { case SNDRV_PCM_SUBFORMAT_MSBITS_20: return 20; case SNDRV_PCM_SUBFORMAT_MSBITS_24: return 24; case SNDRV_PCM_SUBFORMAT_MSBITS_MAX: case SNDRV_PCM_SUBFORMAT_STD: default: break;
}
fallthrough; default: return snd_pcm_format_width(format);
}
}
EXPORT_SYMBOL(snd_pcm_hw_params_bits);
if (substream->runtime->std_sync_id)
snd_pcm_set_sync_per_card(substream, arg, id, sizeof(id)); return 0;
}
/** * snd_pcm_lib_ioctl - a generic PCM ioctl callback * @substream: the pcm substream instance * @cmd: ioctl command * @arg: ioctl argument * * Processes the generic ioctl commands for PCM. * Can be passed as the ioctl callback for PCM ops. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsignedint cmd, void *arg)
{ switch (cmd) { case SNDRV_PCM_IOCTL1_RESET: return snd_pcm_lib_ioctl_reset(substream, arg); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: return snd_pcm_lib_ioctl_channel_info(substream, arg); case SNDRV_PCM_IOCTL1_FIFO_SIZE: return snd_pcm_lib_ioctl_fifo_size(substream, arg); case SNDRV_PCM_IOCTL1_SYNC_ID: return snd_pcm_lib_ioctl_sync_id(substream, arg);
} return -ENXIO;
}
EXPORT_SYMBOL(snd_pcm_lib_ioctl);
/** * snd_pcm_period_elapsed_under_stream_lock() - update the status of runtime for the next period * under acquired lock of PCM substream. * @substream: the instance of pcm substream. * * This function is called when the batch of audio data frames as the same size as the period of * buffer is already processed in audio data transmission. * * The call of function updates the status of runtime with the latest position of audio data * transmission, checks overrun and underrun over buffer, awaken user processes from waiting for * available audio data frames, sampling audio timestamp, and performs stop or drain the PCM * substream according to configured threshold. * * The function is intended to use for the case that PCM driver operates audio data frames under * acquired lock of PCM substream; e.g. in callback of any operation of &snd_pcm_ops in process * context. In any interrupt context, it's preferrable to use ``snd_pcm_period_elapsed()`` instead * since lock of PCM substream should be acquired in advance. * * Developer should pay enough attention that some callbacks in &snd_pcm_ops are done by the call of * function: * * - .pointer - to retrieve current position of audio data transmission by frame count or XRUN state. * - .trigger - with SNDRV_PCM_TRIGGER_STOP at XRUN or DRAINING state. * - .get_time_info - to retrieve audio time stamp if needed. * * Even if more than one periods have elapsed since the last call, you have to call this only once.
*/ void snd_pcm_period_elapsed_under_stream_lock(struct snd_pcm_substream *substream)
{ struct snd_pcm_runtime *runtime;
if (PCM_RUNTIME_CHECK(substream)) return;
runtime = substream->runtime;
if (!snd_pcm_running(substream) ||
snd_pcm_update_hw_ptr0(substream, 1) < 0) goto _end;
/** * snd_pcm_period_elapsed() - update the status of runtime for the next period by acquiring lock of * PCM substream. * @substream: the instance of PCM substream. * * This function is mostly similar to ``snd_pcm_period_elapsed_under_stream_lock()`` except for * acquiring lock of PCM substream voluntarily. * * It's typically called by any type of IRQ handler when hardware IRQ occurs to notify event that * the batch of audio data frames as the same size as the period of buffer is already processed in * audio data transmission.
*/ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
{ if (snd_BUG_ON(!substream)) return;
/* * Wait until avail_min data becomes available * Returns a negative error code if any error occurs during operation. * The available space is stored on availp. When err = 0 and avail = 0 * on the capture stream, it indicates the stream is in DRAINING state.
*/ staticint wait_for_avail(struct snd_pcm_substream *substream,
snd_pcm_uframes_t *availp)
{ struct snd_pcm_runtime *runtime = substream->runtime; int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
wait_queue_entry_t wait; int err = 0;
snd_pcm_uframes_t avail = 0; long wait_time, tout;
if (runtime->no_period_wakeup)
wait_time = MAX_SCHEDULE_TIMEOUT; else { /* use wait time from substream if available */ if (substream->wait_time) {
wait_time = substream->wait_time;
} else {
wait_time = 100;
if (runtime->rate) { long t = runtime->buffer_size * 1100 / runtime->rate;
wait_time = max(t, wait_time);
}
}
wait_time = msecs_to_jiffies(wait_time);
}
for (;;) { if (signal_pending(current)) {
err = -ERESTARTSYS; break;
}
/* * We need to check if space became available already * (and thus the wakeup happened already) first to close * the race of space already having become available. * This check must happen after been added to the waitqueue * and having current state be INTERRUPTIBLE.
*/
avail = snd_pcm_avail(substream); if (avail >= runtime->twake) break;
snd_pcm_stream_unlock_irq(substream);
tout = schedule_timeout(wait_time);
snd_pcm_stream_lock_irq(substream);
set_current_state(TASK_INTERRUPTIBLE); switch (runtime->state) { case SNDRV_PCM_STATE_SUSPENDED:
err = -ESTRPIPE; goto _endloop; case SNDRV_PCM_STATE_XRUN:
err = -EPIPE; goto _endloop; case SNDRV_PCM_STATE_DRAINING: if (is_playback)
err = -EPIPE; else
avail = 0; /* indicate draining */ goto _endloop; case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_DISCONNECTED:
err = -EBADFD; goto _endloop; case SNDRV_PCM_STATE_PAUSED: continue;
} if (!tout) {
pcm_dbg(substream->pcm, "%s timeout (DMA or IRQ trouble?)\n",
is_playback ? "playback write" : "capture read");
err = -EIO; break;
}
}
_endloop:
set_current_state(TASK_RUNNING);
remove_wait_queue(&runtime->tsleep, &wait);
*availp = avail; return err;
}
/* calculate the target DMA-buffer position to be written/read */ staticvoid *get_dma_ptr(struct snd_pcm_runtime *runtime, int channel, unsignedlong hwoff)
{ return runtime->dma_area + hwoff +
channel * (runtime->dma_bytes / runtime->channels);
}
/* default copy ops for write; used for both interleaved and non- modes */ staticint default_write_copy(struct snd_pcm_substream *substream, int channel, unsignedlong hwoff, struct iov_iter *iter, unsignedlong bytes)
{ if (copy_from_iter(get_dma_ptr(substream->runtime, channel, hwoff),
bytes, iter) != bytes) return -EFAULT; return 0;
}
/* fill silence instead of copy data; called as a transfer helper * from __snd_pcm_lib_write() or directly from noninterleaved_copy() when * a NULL buffer is passed
*/ staticint fill_silence(struct snd_pcm_substream *substream, int channel, unsignedlong hwoff, struct iov_iter *iter, unsignedlong bytes)
{ struct snd_pcm_runtime *runtime = substream->runtime;
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return 0; if (substream->ops->fill_silence) return substream->ops->fill_silence(substream, channel,
hwoff, bytes);
/* call transfer function with the converted pointers and sizes; * for interleaved mode, it's one shot for all samples
*/ staticint interleaved_copy(struct snd_pcm_substream *substream,
snd_pcm_uframes_t hwoff, void *data,
snd_pcm_uframes_t off,
snd_pcm_uframes_t frames,
pcm_transfer_f transfer, bool in_kernel)
{ struct snd_pcm_runtime *runtime = substream->runtime;
/* convert to bytes */
hwoff = frames_to_bytes(runtime, hwoff);
off = frames_to_bytes(runtime, off);
frames = frames_to_bytes(runtime, frames);
/* call transfer function with the converted pointers and sizes for each * non-interleaved channel; when buffer is NULL, silencing instead of copying
*/ staticint noninterleaved_copy(struct snd_pcm_substream *substream,
snd_pcm_uframes_t hwoff, void *data,
snd_pcm_uframes_t off,
snd_pcm_uframes_t frames,
pcm_transfer_f transfer, bool in_kernel)
{ struct snd_pcm_runtime *runtime = substream->runtime; int channels = runtime->channels; void **bufs = data; int c, err;
/* convert to bytes; note that it's not frames_to_bytes() here. * in non-interleaved mode, we copy for each channel, thus * each copy is n_samples bytes x channels = whole frames.
*/
off = samples_to_bytes(runtime, off);
frames = samples_to_bytes(runtime, frames);
hwoff = samples_to_bytes(runtime, hwoff); for (c = 0; c < channels; ++c, ++bufs) { if (!data || !*bufs)
err = fill_silence(substream, c, hwoff, NULL, frames); else
err = do_transfer(substream, c, hwoff, *bufs + off,
frames, transfer, in_kernel); if (err < 0) return err;
} return 0;
}
/* fill silence on the given buffer position; * called from snd_pcm_playback_silence()
*/ staticint fill_silence_frames(struct snd_pcm_substream *substream,
snd_pcm_uframes_t off, snd_pcm_uframes_t frames)
{ if (substream->runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
substream->runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) return interleaved_copy(substream, off, NULL, 0, frames,
fill_silence, true); else return noninterleaved_copy(substream, off, NULL, 0, frames,
fill_silence, true);
}
/* sanity-check for read/write methods */ staticint pcm_sanity_check(struct snd_pcm_substream *substream)
{ struct snd_pcm_runtime *runtime; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO;
runtime = substream->runtime; if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) return -EINVAL; if (runtime->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; return 0;
}
staticint pcm_accessible_state(struct snd_pcm_runtime *runtime)
{ switch (runtime->state) { case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_PAUSED: return 0; case SNDRV_PCM_STATE_XRUN: return -EPIPE; case SNDRV_PCM_STATE_SUSPENDED: return -ESTRPIPE; default: return -EBADFD;
}
}
/* update to the given appl_ptr and call ack callback if needed; * when an error is returned, take back to the original value
*/ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
snd_pcm_uframes_t appl_ptr)
{ struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr;
snd_pcm_sframes_t diff; int ret;
if (old_appl_ptr == appl_ptr) return 0;
if (appl_ptr >= runtime->boundary) return -EINVAL; /* * check if a rewind is requested by the application
*/ if (substream->runtime->info & SNDRV_PCM_INFO_NO_REWINDS) {
diff = appl_ptr - old_appl_ptr; if (diff >= 0) { if (diff > runtime->buffer_size) return -EINVAL;
} else { if (runtime->boundary + diff > runtime->buffer_size) return -EINVAL;
}
}
runtime->control->appl_ptr = appl_ptr; if (substream->ops->ack) {
ret = substream->ops->ack(substream); if (ret < 0) {
runtime->control->appl_ptr = old_appl_ptr; if (ret == -EPIPE)
__snd_pcm_xrun(substream); return ret;
}
}
trace_applptr(substream, old_appl_ptr, appl_ptr);
return 0;
}
/* the common loop for read/write data */
snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, void *data, bool interleaved,
snd_pcm_uframes_t size, bool in_kernel)
{ struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t xfer = 0;
snd_pcm_uframes_t offset = 0;
snd_pcm_uframes_t avail;
pcm_copy_f writer;
pcm_transfer_f transfer; bool nonblock; bool is_playback; int err;
err = pcm_sanity_check(substream); if (err < 0) return err;
if (!data) { if (is_playback)
transfer = fill_silence; else return -EINVAL;
} else { if (substream->ops->copy)
transfer = substream->ops->copy; else
transfer = is_playback ?
default_write_copy : default_read_copy;
}
if (size == 0) return 0;
nonblock = !!(substream->f_flags & O_NONBLOCK);
snd_pcm_stream_lock_irq(substream);
err = pcm_accessible_state(runtime); if (err < 0) goto _end_unlock;
/** * snd_pcm_add_chmap_ctls - create channel-mapping control elements * @pcm: the assigned PCM instance * @stream: stream direction * @chmap: channel map elements (for query) * @max_channels: the max number of channels for the stream * @private_value: the value passed to each kcontrol's private_value field * @info_ret: store struct snd_pcm_chmap instance if non-NULL * * Create channel-mapping control elements assigned to the given PCM stream(s). * Return: Zero if successful, or a negative error value.
*/ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, conststruct snd_pcm_chmap_elem *chmap, int max_channels, unsignedlong private_value, struct snd_pcm_chmap **info_ret)
{ struct snd_pcm_chmap *info; struct snd_kcontrol_new knew = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
.info = pcm_chmap_ctl_info,
.get = pcm_chmap_ctl_get,
.tlv.c = pcm_chmap_ctl_tlv,
}; int err;
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.