diff options
Diffstat (limited to 'sound/core/pcm_native.c')
-rw-r--r-- | sound/core/pcm_native.c | 1013 |
1 files changed, 559 insertions, 454 deletions
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index faa2e2be6f2e..b3d5bed75029 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c | |||
@@ -37,6 +37,18 @@ | |||
37 | #include <sound/minors.h> | 37 | #include <sound/minors.h> |
38 | #include <linux/uio.h> | 38 | #include <linux/uio.h> |
39 | 39 | ||
40 | #include "pcm_local.h" | ||
41 | |||
42 | #ifdef CONFIG_SND_DEBUG | ||
43 | #define CREATE_TRACE_POINTS | ||
44 | #include "pcm_param_trace.h" | ||
45 | #else | ||
46 | #define trace_hw_mask_param_enabled() 0 | ||
47 | #define trace_hw_interval_param_enabled() 0 | ||
48 | #define trace_hw_mask_param(substream, type, index, prev, curr) | ||
49 | #define trace_hw_interval_param(substream, type, index, prev, curr) | ||
50 | #endif | ||
51 | |||
40 | /* | 52 | /* |
41 | * Compatibility | 53 | * Compatibility |
42 | */ | 54 | */ |
@@ -181,20 +193,6 @@ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, | |||
181 | } | 193 | } |
182 | EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); | 194 | EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); |
183 | 195 | ||
184 | static inline mm_segment_t snd_enter_user(void) | ||
185 | { | ||
186 | mm_segment_t fs = get_fs(); | ||
187 | set_fs(get_ds()); | ||
188 | return fs; | ||
189 | } | ||
190 | |||
191 | static inline void snd_leave_user(mm_segment_t fs) | ||
192 | { | ||
193 | set_fs(fs); | ||
194 | } | ||
195 | |||
196 | |||
197 | |||
198 | int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) | 196 | int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) |
199 | { | 197 | { |
200 | struct snd_pcm_runtime *runtime; | 198 | struct snd_pcm_runtime *runtime; |
@@ -214,11 +212,7 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) | |||
214 | info->subdevices_avail = pstr->substream_count - pstr->substream_opened; | 212 | info->subdevices_avail = pstr->substream_count - pstr->substream_opened; |
215 | strlcpy(info->subname, substream->name, sizeof(info->subname)); | 213 | strlcpy(info->subname, substream->name, sizeof(info->subname)); |
216 | runtime = substream->runtime; | 214 | runtime = substream->runtime; |
217 | /* AB: FIXME!!! This is definitely nonsense */ | 215 | |
218 | if (runtime) { | ||
219 | info->sync = runtime->sync; | ||
220 | substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); | ||
221 | } | ||
222 | return 0; | 216 | return 0; |
223 | } | 217 | } |
224 | 218 | ||
@@ -255,205 +249,268 @@ static bool hw_support_mmap(struct snd_pcm_substream *substream) | |||
255 | return true; | 249 | return true; |
256 | } | 250 | } |
257 | 251 | ||
258 | #undef RULES_DEBUG | 252 | static int constrain_mask_params(struct snd_pcm_substream *substream, |
259 | 253 | struct snd_pcm_hw_params *params) | |
260 | #ifdef RULES_DEBUG | ||
261 | #define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v | ||
262 | static const char * const snd_pcm_hw_param_names[] = { | ||
263 | HW_PARAM(ACCESS), | ||
264 | HW_PARAM(FORMAT), | ||
265 | HW_PARAM(SUBFORMAT), | ||
266 | HW_PARAM(SAMPLE_BITS), | ||
267 | HW_PARAM(FRAME_BITS), | ||
268 | HW_PARAM(CHANNELS), | ||
269 | HW_PARAM(RATE), | ||
270 | HW_PARAM(PERIOD_TIME), | ||
271 | HW_PARAM(PERIOD_SIZE), | ||
272 | HW_PARAM(PERIOD_BYTES), | ||
273 | HW_PARAM(PERIODS), | ||
274 | HW_PARAM(BUFFER_TIME), | ||
275 | HW_PARAM(BUFFER_SIZE), | ||
276 | HW_PARAM(BUFFER_BYTES), | ||
277 | HW_PARAM(TICK_TIME), | ||
278 | }; | ||
279 | #endif | ||
280 | |||
281 | int snd_pcm_hw_refine(struct snd_pcm_substream *substream, | ||
282 | struct snd_pcm_hw_params *params) | ||
283 | { | 254 | { |
255 | struct snd_pcm_hw_constraints *constrs = | ||
256 | &substream->runtime->hw_constraints; | ||
257 | struct snd_mask *m; | ||
284 | unsigned int k; | 258 | unsigned int k; |
285 | struct snd_pcm_hardware *hw; | 259 | struct snd_mask old_mask; |
286 | struct snd_interval *i = NULL; | 260 | int changed; |
287 | struct snd_mask *m = NULL; | ||
288 | struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; | ||
289 | unsigned int rstamps[constrs->rules_num]; | ||
290 | unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; | ||
291 | unsigned int stamp = 2; | ||
292 | int changed, again; | ||
293 | |||
294 | params->info = 0; | ||
295 | params->fifo_size = 0; | ||
296 | if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) | ||
297 | params->msbits = 0; | ||
298 | if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { | ||
299 | params->rate_num = 0; | ||
300 | params->rate_den = 0; | ||
301 | } | ||
302 | 261 | ||
303 | for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { | 262 | for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { |
304 | m = hw_param_mask(params, k); | 263 | m = hw_param_mask(params, k); |
305 | if (snd_mask_empty(m)) | 264 | if (snd_mask_empty(m)) |
306 | return -EINVAL; | 265 | return -EINVAL; |
266 | |||
267 | /* This parameter is not requested to change by a caller. */ | ||
307 | if (!(params->rmask & (1 << k))) | 268 | if (!(params->rmask & (1 << k))) |
308 | continue; | 269 | continue; |
309 | #ifdef RULES_DEBUG | 270 | |
310 | pr_debug("%s = ", snd_pcm_hw_param_names[k]); | 271 | if (trace_hw_mask_param_enabled()) |
311 | pr_cont("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); | 272 | old_mask = *m; |
312 | #endif | 273 | |
313 | changed = snd_mask_refine(m, constrs_mask(constrs, k)); | 274 | changed = snd_mask_refine(m, constrs_mask(constrs, k)); |
314 | #ifdef RULES_DEBUG | ||
315 | pr_cont("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); | ||
316 | #endif | ||
317 | if (changed) | ||
318 | params->cmask |= 1 << k; | ||
319 | if (changed < 0) | 275 | if (changed < 0) |
320 | return changed; | 276 | return changed; |
277 | if (changed == 0) | ||
278 | continue; | ||
279 | |||
280 | /* Set corresponding flag so that the caller gets it. */ | ||
281 | trace_hw_mask_param(substream, k, 0, &old_mask, m); | ||
282 | params->cmask |= 1 << k; | ||
321 | } | 283 | } |
322 | 284 | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int constrain_interval_params(struct snd_pcm_substream *substream, | ||
289 | struct snd_pcm_hw_params *params) | ||
290 | { | ||
291 | struct snd_pcm_hw_constraints *constrs = | ||
292 | &substream->runtime->hw_constraints; | ||
293 | struct snd_interval *i; | ||
294 | unsigned int k; | ||
295 | struct snd_interval old_interval; | ||
296 | int changed; | ||
297 | |||
323 | for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { | 298 | for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { |
324 | i = hw_param_interval(params, k); | 299 | i = hw_param_interval(params, k); |
325 | if (snd_interval_empty(i)) | 300 | if (snd_interval_empty(i)) |
326 | return -EINVAL; | 301 | return -EINVAL; |
302 | |||
303 | /* This parameter is not requested to change by a caller. */ | ||
327 | if (!(params->rmask & (1 << k))) | 304 | if (!(params->rmask & (1 << k))) |
328 | continue; | 305 | continue; |
329 | #ifdef RULES_DEBUG | 306 | |
330 | pr_debug("%s = ", snd_pcm_hw_param_names[k]); | 307 | if (trace_hw_interval_param_enabled()) |
331 | if (i->empty) | 308 | old_interval = *i; |
332 | pr_cont("empty"); | 309 | |
333 | else | ||
334 | pr_cont("%c%u %u%c", | ||
335 | i->openmin ? '(' : '[', i->min, | ||
336 | i->max, i->openmax ? ')' : ']'); | ||
337 | pr_cont(" -> "); | ||
338 | #endif | ||
339 | changed = snd_interval_refine(i, constrs_interval(constrs, k)); | 310 | changed = snd_interval_refine(i, constrs_interval(constrs, k)); |
340 | #ifdef RULES_DEBUG | ||
341 | if (i->empty) | ||
342 | pr_cont("empty\n"); | ||
343 | else | ||
344 | pr_cont("%c%u %u%c\n", | ||
345 | i->openmin ? '(' : '[', i->min, | ||
346 | i->max, i->openmax ? ')' : ']'); | ||
347 | #endif | ||
348 | if (changed) | ||
349 | params->cmask |= 1 << k; | ||
350 | if (changed < 0) | 311 | if (changed < 0) |
351 | return changed; | 312 | return changed; |
313 | if (changed == 0) | ||
314 | continue; | ||
315 | |||
316 | /* Set corresponding flag so that the caller gets it. */ | ||
317 | trace_hw_interval_param(substream, k, 0, &old_interval, i); | ||
318 | params->cmask |= 1 << k; | ||
352 | } | 319 | } |
353 | 320 | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int constrain_params_by_rules(struct snd_pcm_substream *substream, | ||
325 | struct snd_pcm_hw_params *params) | ||
326 | { | ||
327 | struct snd_pcm_hw_constraints *constrs = | ||
328 | &substream->runtime->hw_constraints; | ||
329 | unsigned int k; | ||
330 | unsigned int rstamps[constrs->rules_num]; | ||
331 | unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; | ||
332 | unsigned int stamp; | ||
333 | struct snd_pcm_hw_rule *r; | ||
334 | unsigned int d; | ||
335 | struct snd_mask old_mask; | ||
336 | struct snd_interval old_interval; | ||
337 | bool again; | ||
338 | int changed; | ||
339 | |||
340 | /* | ||
341 | * Each application of rule has own sequence number. | ||
342 | * | ||
343 | * Each member of 'rstamps' array represents the sequence number of | ||
344 | * recent application of corresponding rule. | ||
345 | */ | ||
354 | for (k = 0; k < constrs->rules_num; k++) | 346 | for (k = 0; k < constrs->rules_num; k++) |
355 | rstamps[k] = 0; | 347 | rstamps[k] = 0; |
356 | for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) | 348 | |
349 | /* | ||
350 | * Each member of 'vstamps' array represents the sequence number of | ||
351 | * recent application of rule in which corresponding parameters were | ||
352 | * changed. | ||
353 | * | ||
354 | * In initial state, elements corresponding to parameters requested by | ||
355 | * a caller is 1. For unrequested parameters, corresponding members | ||
356 | * have 0 so that the parameters are never changed anymore. | ||
357 | */ | ||
358 | for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) | ||
357 | vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; | 359 | vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; |
358 | do { | 360 | |
359 | again = 0; | 361 | /* Due to the above design, actual sequence number starts at 2. */ |
360 | for (k = 0; k < constrs->rules_num; k++) { | 362 | stamp = 2; |
361 | struct snd_pcm_hw_rule *r = &constrs->rules[k]; | 363 | retry: |
362 | unsigned int d; | 364 | /* Apply all rules in order. */ |
363 | int doit = 0; | 365 | again = false; |
364 | if (r->cond && !(r->cond & params->flags)) | 366 | for (k = 0; k < constrs->rules_num; k++) { |
365 | continue; | 367 | r = &constrs->rules[k]; |
366 | for (d = 0; r->deps[d] >= 0; d++) { | 368 | |
367 | if (vstamps[r->deps[d]] > rstamps[k]) { | 369 | /* |
368 | doit = 1; | 370 | * Check condition bits of this rule. When the rule has |
369 | break; | 371 | * some condition bits, parameter without the bits is |
370 | } | 372 | * never processed. SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP |
371 | } | 373 | * is an example of the condition bits. |
372 | if (!doit) | 374 | */ |
373 | continue; | 375 | if (r->cond && !(r->cond & params->flags)) |
374 | #ifdef RULES_DEBUG | 376 | continue; |
375 | pr_debug("Rule %d [%p]: ", k, r->func); | 377 | |
376 | if (r->var >= 0) { | 378 | /* |
377 | pr_cont("%s = ", snd_pcm_hw_param_names[r->var]); | 379 | * The 'deps' array includes maximum three dependencies |
378 | if (hw_is_mask(r->var)) { | 380 | * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fourth |
379 | m = hw_param_mask(params, r->var); | 381 | * member of this array is a sentinel and should be |
380 | pr_cont("%x", *m->bits); | 382 | * negative value. |
381 | } else { | 383 | * |
382 | i = hw_param_interval(params, r->var); | 384 | * This rule should be processed in this time when dependent |
383 | if (i->empty) | 385 | * parameters were changed at former applications of the other |
384 | pr_cont("empty"); | 386 | * rules. |
385 | else | 387 | */ |
386 | pr_cont("%c%u %u%c", | 388 | for (d = 0; r->deps[d] >= 0; d++) { |
387 | i->openmin ? '(' : '[', i->min, | 389 | if (vstamps[r->deps[d]] > rstamps[k]) |
388 | i->max, i->openmax ? ')' : ']'); | 390 | break; |
389 | } | 391 | } |
390 | } | 392 | if (r->deps[d] < 0) |
391 | #endif | 393 | continue; |
392 | changed = r->func(params, r); | 394 | |
393 | #ifdef RULES_DEBUG | 395 | if (trace_hw_mask_param_enabled()) { |
394 | if (r->var >= 0) { | 396 | if (hw_is_mask(r->var)) |
395 | pr_cont(" -> "); | 397 | old_mask = *hw_param_mask(params, r->var); |
396 | if (hw_is_mask(r->var)) | 398 | } |
397 | pr_cont("%x", *m->bits); | 399 | if (trace_hw_interval_param_enabled()) { |
398 | else { | 400 | if (hw_is_interval(r->var)) |
399 | if (i->empty) | 401 | old_interval = *hw_param_interval(params, r->var); |
400 | pr_cont("empty"); | 402 | } |
401 | else | 403 | |
402 | pr_cont("%c%u %u%c", | 404 | changed = r->func(params, r); |
403 | i->openmin ? '(' : '[', i->min, | 405 | if (changed < 0) |
404 | i->max, i->openmax ? ')' : ']'); | 406 | return changed; |
405 | } | 407 | |
408 | /* | ||
409 | * When the parameter is changed, notify it to the caller | ||
410 | * by corresponding returned bit, then preparing for next | ||
411 | * iteration. | ||
412 | */ | ||
413 | if (changed && r->var >= 0) { | ||
414 | if (hw_is_mask(r->var)) { | ||
415 | trace_hw_mask_param(substream, r->var, | ||
416 | k + 1, &old_mask, | ||
417 | hw_param_mask(params, r->var)); | ||
406 | } | 418 | } |
407 | pr_cont("\n"); | 419 | if (hw_is_interval(r->var)) { |
408 | #endif | 420 | trace_hw_interval_param(substream, r->var, |
409 | rstamps[k] = stamp; | 421 | k + 1, &old_interval, |
410 | if (changed && r->var >= 0) { | 422 | hw_param_interval(params, r->var)); |
411 | params->cmask |= (1 << r->var); | ||
412 | vstamps[r->var] = stamp; | ||
413 | again = 1; | ||
414 | } | 423 | } |
415 | if (changed < 0) | 424 | |
416 | return changed; | 425 | params->cmask |= (1 << r->var); |
417 | stamp++; | 426 | vstamps[r->var] = stamp; |
427 | again = true; | ||
418 | } | 428 | } |
419 | } while (again); | 429 | |
430 | rstamps[k] = stamp++; | ||
431 | } | ||
432 | |||
433 | /* Iterate to evaluate all rules till no parameters are changed. */ | ||
434 | if (again) | ||
435 | goto retry; | ||
436 | |||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | static int fixup_unreferenced_params(struct snd_pcm_substream *substream, | ||
441 | struct snd_pcm_hw_params *params) | ||
442 | { | ||
443 | const struct snd_interval *i; | ||
444 | const struct snd_mask *m; | ||
445 | int err; | ||
446 | |||
420 | if (!params->msbits) { | 447 | if (!params->msbits) { |
421 | i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); | 448 | i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); |
422 | if (snd_interval_single(i)) | 449 | if (snd_interval_single(i)) |
423 | params->msbits = snd_interval_value(i); | 450 | params->msbits = snd_interval_value(i); |
424 | } | 451 | } |
425 | 452 | ||
426 | if (!params->rate_den) { | 453 | if (!params->rate_den) { |
427 | i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | 454 | i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); |
428 | if (snd_interval_single(i)) { | 455 | if (snd_interval_single(i)) { |
429 | params->rate_num = snd_interval_value(i); | 456 | params->rate_num = snd_interval_value(i); |
430 | params->rate_den = 1; | 457 | params->rate_den = 1; |
431 | } | 458 | } |
432 | } | 459 | } |
433 | 460 | ||
434 | hw = &substream->runtime->hw; | 461 | if (!params->fifo_size) { |
462 | m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); | ||
463 | i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
464 | if (snd_mask_single(m) && snd_interval_single(i)) { | ||
465 | err = substream->ops->ioctl(substream, | ||
466 | SNDRV_PCM_IOCTL1_FIFO_SIZE, params); | ||
467 | if (err < 0) | ||
468 | return err; | ||
469 | } | ||
470 | } | ||
471 | |||
435 | if (!params->info) { | 472 | if (!params->info) { |
436 | params->info = hw->info & ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | | 473 | params->info = substream->runtime->hw.info; |
437 | SNDRV_PCM_INFO_DRAIN_TRIGGER); | 474 | params->info &= ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | |
475 | SNDRV_PCM_INFO_DRAIN_TRIGGER); | ||
438 | if (!hw_support_mmap(substream)) | 476 | if (!hw_support_mmap(substream)) |
439 | params->info &= ~(SNDRV_PCM_INFO_MMAP | | 477 | params->info &= ~(SNDRV_PCM_INFO_MMAP | |
440 | SNDRV_PCM_INFO_MMAP_VALID); | 478 | SNDRV_PCM_INFO_MMAP_VALID); |
441 | } | 479 | } |
442 | if (!params->fifo_size) { | 480 | |
443 | m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); | 481 | return 0; |
444 | i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | 482 | } |
445 | if (snd_mask_min(m) == snd_mask_max(m) && | 483 | |
446 | snd_interval_min(i) == snd_interval_max(i)) { | 484 | int snd_pcm_hw_refine(struct snd_pcm_substream *substream, |
447 | changed = substream->ops->ioctl(substream, | 485 | struct snd_pcm_hw_params *params) |
448 | SNDRV_PCM_IOCTL1_FIFO_SIZE, params); | 486 | { |
449 | if (changed < 0) | 487 | int err; |
450 | return changed; | 488 | |
451 | } | 489 | params->info = 0; |
490 | params->fifo_size = 0; | ||
491 | if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) | ||
492 | params->msbits = 0; | ||
493 | if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { | ||
494 | params->rate_num = 0; | ||
495 | params->rate_den = 0; | ||
452 | } | 496 | } |
497 | |||
498 | err = constrain_mask_params(substream, params); | ||
499 | if (err < 0) | ||
500 | return err; | ||
501 | |||
502 | err = constrain_interval_params(substream, params); | ||
503 | if (err < 0) | ||
504 | return err; | ||
505 | |||
506 | err = constrain_params_by_rules(substream, params); | ||
507 | if (err < 0) | ||
508 | return err; | ||
509 | |||
453 | params->rmask = 0; | 510 | params->rmask = 0; |
511 | |||
454 | return 0; | 512 | return 0; |
455 | } | 513 | } |
456 | |||
457 | EXPORT_SYMBOL(snd_pcm_hw_refine); | 514 | EXPORT_SYMBOL(snd_pcm_hw_refine); |
458 | 515 | ||
459 | static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, | 516 | static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, |
@@ -467,11 +524,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, | |||
467 | return PTR_ERR(params); | 524 | return PTR_ERR(params); |
468 | 525 | ||
469 | err = snd_pcm_hw_refine(substream, params); | 526 | err = snd_pcm_hw_refine(substream, params); |
470 | if (copy_to_user(_params, params, sizeof(*params))) { | 527 | if (err < 0) |
471 | if (!err) | 528 | goto end; |
472 | err = -EFAULT; | 529 | |
473 | } | 530 | err = fixup_unreferenced_params(substream, params); |
531 | if (err < 0) | ||
532 | goto end; | ||
474 | 533 | ||
534 | if (copy_to_user(_params, params, sizeof(*params))) | ||
535 | err = -EFAULT; | ||
536 | end: | ||
475 | kfree(params); | 537 | kfree(params); |
476 | return err; | 538 | return err; |
477 | } | 539 | } |
@@ -509,6 +571,70 @@ static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream, | |||
509 | #endif | 571 | #endif |
510 | } | 572 | } |
511 | 573 | ||
574 | /** | ||
575 | * snd_pcm_hw_param_choose - choose a configuration defined by @params | ||
576 | * @pcm: PCM instance | ||
577 | * @params: the hw_params instance | ||
578 | * | ||
579 | * Choose one configuration from configuration space defined by @params. | ||
580 | * The configuration chosen is that obtained fixing in this order: | ||
581 | * first access, first format, first subformat, min channels, | ||
582 | * min rate, min period time, max buffer size, min tick time | ||
583 | * | ||
584 | * Return: Zero if successful, or a negative error code on failure. | ||
585 | */ | ||
586 | static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, | ||
587 | struct snd_pcm_hw_params *params) | ||
588 | { | ||
589 | static const int vars[] = { | ||
590 | SNDRV_PCM_HW_PARAM_ACCESS, | ||
591 | SNDRV_PCM_HW_PARAM_FORMAT, | ||
592 | SNDRV_PCM_HW_PARAM_SUBFORMAT, | ||
593 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
594 | SNDRV_PCM_HW_PARAM_RATE, | ||
595 | SNDRV_PCM_HW_PARAM_PERIOD_TIME, | ||
596 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, | ||
597 | SNDRV_PCM_HW_PARAM_TICK_TIME, | ||
598 | -1 | ||
599 | }; | ||
600 | const int *v; | ||
601 | struct snd_mask old_mask; | ||
602 | struct snd_interval old_interval; | ||
603 | int changed; | ||
604 | |||
605 | for (v = vars; *v != -1; v++) { | ||
606 | /* Keep old parameter to trace. */ | ||
607 | if (trace_hw_mask_param_enabled()) { | ||
608 | if (hw_is_mask(*v)) | ||
609 | old_mask = *hw_param_mask(params, *v); | ||
610 | } | ||
611 | if (trace_hw_interval_param_enabled()) { | ||
612 | if (hw_is_interval(*v)) | ||
613 | old_interval = *hw_param_interval(params, *v); | ||
614 | } | ||
615 | if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) | ||
616 | changed = snd_pcm_hw_param_first(pcm, params, *v, NULL); | ||
617 | else | ||
618 | changed = snd_pcm_hw_param_last(pcm, params, *v, NULL); | ||
619 | if (snd_BUG_ON(changed < 0)) | ||
620 | return changed; | ||
621 | if (changed == 0) | ||
622 | continue; | ||
623 | |||
624 | /* Trace the changed parameter. */ | ||
625 | if (hw_is_mask(*v)) { | ||
626 | trace_hw_mask_param(pcm, *v, 0, &old_mask, | ||
627 | hw_param_mask(params, *v)); | ||
628 | } | ||
629 | if (hw_is_interval(*v)) { | ||
630 | trace_hw_interval_param(pcm, *v, 0, &old_interval, | ||
631 | hw_param_interval(params, *v)); | ||
632 | } | ||
633 | } | ||
634 | |||
635 | return 0; | ||
636 | } | ||
637 | |||
512 | static int snd_pcm_hw_params(struct snd_pcm_substream *substream, | 638 | static int snd_pcm_hw_params(struct snd_pcm_substream *substream, |
513 | struct snd_pcm_hw_params *params) | 639 | struct snd_pcm_hw_params *params) |
514 | { | 640 | { |
@@ -546,6 +672,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, | |||
546 | if (err < 0) | 672 | if (err < 0) |
547 | goto _error; | 673 | goto _error; |
548 | 674 | ||
675 | err = fixup_unreferenced_params(substream, params); | ||
676 | if (err < 0) | ||
677 | goto _error; | ||
678 | |||
549 | if (substream->ops->hw_params != NULL) { | 679 | if (substream->ops->hw_params != NULL) { |
550 | err = substream->ops->hw_params(substream, params); | 680 | err = substream->ops->hw_params(substream, params); |
551 | if (err < 0) | 681 | if (err < 0) |
@@ -621,11 +751,12 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, | |||
621 | return PTR_ERR(params); | 751 | return PTR_ERR(params); |
622 | 752 | ||
623 | err = snd_pcm_hw_params(substream, params); | 753 | err = snd_pcm_hw_params(substream, params); |
624 | if (copy_to_user(_params, params, sizeof(*params))) { | 754 | if (err < 0) |
625 | if (!err) | 755 | goto end; |
626 | err = -EFAULT; | ||
627 | } | ||
628 | 756 | ||
757 | if (copy_to_user(_params, params, sizeof(*params))) | ||
758 | err = -EFAULT; | ||
759 | end: | ||
629 | kfree(params); | 760 | kfree(params); |
630 | return err; | 761 | return err; |
631 | } | 762 | } |
@@ -1081,6 +1212,7 @@ static const struct action_ops snd_pcm_action_start = { | |||
1081 | * @substream: the PCM substream instance | 1212 | * @substream: the PCM substream instance |
1082 | * | 1213 | * |
1083 | * Return: Zero if successful, or a negative error code. | 1214 | * Return: Zero if successful, or a negative error code. |
1215 | * The stream lock must be acquired before calling this function. | ||
1084 | */ | 1216 | */ |
1085 | int snd_pcm_start(struct snd_pcm_substream *substream) | 1217 | int snd_pcm_start(struct snd_pcm_substream *substream) |
1086 | { | 1218 | { |
@@ -1088,6 +1220,13 @@ int snd_pcm_start(struct snd_pcm_substream *substream) | |||
1088 | SNDRV_PCM_STATE_RUNNING); | 1220 | SNDRV_PCM_STATE_RUNNING); |
1089 | } | 1221 | } |
1090 | 1222 | ||
1223 | /* take the stream lock and start the streams */ | ||
1224 | static int snd_pcm_start_lock_irq(struct snd_pcm_substream *substream) | ||
1225 | { | ||
1226 | return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, | ||
1227 | SNDRV_PCM_STATE_RUNNING); | ||
1228 | } | ||
1229 | |||
1091 | /* | 1230 | /* |
1092 | * stop callbacks | 1231 | * stop callbacks |
1093 | */ | 1232 | */ |
@@ -1139,7 +1278,6 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) | |||
1139 | { | 1278 | { |
1140 | return snd_pcm_action(&snd_pcm_action_stop, substream, state); | 1279 | return snd_pcm_action(&snd_pcm_action_stop, substream, state); |
1141 | } | 1280 | } |
1142 | |||
1143 | EXPORT_SYMBOL(snd_pcm_stop); | 1281 | EXPORT_SYMBOL(snd_pcm_stop); |
1144 | 1282 | ||
1145 | /** | 1283 | /** |
@@ -1314,7 +1452,6 @@ int snd_pcm_suspend(struct snd_pcm_substream *substream) | |||
1314 | snd_pcm_stream_unlock_irqrestore(substream, flags); | 1452 | snd_pcm_stream_unlock_irqrestore(substream, flags); |
1315 | return err; | 1453 | return err; |
1316 | } | 1454 | } |
1317 | |||
1318 | EXPORT_SYMBOL(snd_pcm_suspend); | 1455 | EXPORT_SYMBOL(snd_pcm_suspend); |
1319 | 1456 | ||
1320 | /** | 1457 | /** |
@@ -1346,7 +1483,6 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm) | |||
1346 | } | 1483 | } |
1347 | return 0; | 1484 | return 0; |
1348 | } | 1485 | } |
1349 | |||
1350 | EXPORT_SYMBOL(snd_pcm_suspend_all); | 1486 | EXPORT_SYMBOL(snd_pcm_suspend_all); |
1351 | 1487 | ||
1352 | /* resume */ | 1488 | /* resume */ |
@@ -1397,14 +1533,7 @@ static const struct action_ops snd_pcm_action_resume = { | |||
1397 | 1533 | ||
1398 | static int snd_pcm_resume(struct snd_pcm_substream *substream) | 1534 | static int snd_pcm_resume(struct snd_pcm_substream *substream) |
1399 | { | 1535 | { |
1400 | struct snd_card *card = substream->pcm->card; | 1536 | return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); |
1401 | int res; | ||
1402 | |||
1403 | snd_power_lock(card); | ||
1404 | if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) | ||
1405 | res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); | ||
1406 | snd_power_unlock(card); | ||
1407 | return res; | ||
1408 | } | 1537 | } |
1409 | 1538 | ||
1410 | #else | 1539 | #else |
@@ -1423,17 +1552,9 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream) | |||
1423 | */ | 1552 | */ |
1424 | static int snd_pcm_xrun(struct snd_pcm_substream *substream) | 1553 | static int snd_pcm_xrun(struct snd_pcm_substream *substream) |
1425 | { | 1554 | { |
1426 | struct snd_card *card = substream->pcm->card; | ||
1427 | struct snd_pcm_runtime *runtime = substream->runtime; | 1555 | struct snd_pcm_runtime *runtime = substream->runtime; |
1428 | int result; | 1556 | int result; |
1429 | 1557 | ||
1430 | snd_power_lock(card); | ||
1431 | if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { | ||
1432 | result = snd_power_wait(card, SNDRV_CTL_POWER_D0); | ||
1433 | if (result < 0) | ||
1434 | goto _unlock; | ||
1435 | } | ||
1436 | |||
1437 | snd_pcm_stream_lock_irq(substream); | 1558 | snd_pcm_stream_lock_irq(substream); |
1438 | switch (runtime->status->state) { | 1559 | switch (runtime->status->state) { |
1439 | case SNDRV_PCM_STATE_XRUN: | 1560 | case SNDRV_PCM_STATE_XRUN: |
@@ -1446,8 +1567,6 @@ static int snd_pcm_xrun(struct snd_pcm_substream *substream) | |||
1446 | result = -EBADFD; | 1567 | result = -EBADFD; |
1447 | } | 1568 | } |
1448 | snd_pcm_stream_unlock_irq(substream); | 1569 | snd_pcm_stream_unlock_irq(substream); |
1449 | _unlock: | ||
1450 | snd_power_unlock(card); | ||
1451 | return result; | 1570 | return result; |
1452 | } | 1571 | } |
1453 | 1572 | ||
@@ -1551,8 +1670,6 @@ static const struct action_ops snd_pcm_action_prepare = { | |||
1551 | static int snd_pcm_prepare(struct snd_pcm_substream *substream, | 1670 | static int snd_pcm_prepare(struct snd_pcm_substream *substream, |
1552 | struct file *file) | 1671 | struct file *file) |
1553 | { | 1672 | { |
1554 | int res; | ||
1555 | struct snd_card *card = substream->pcm->card; | ||
1556 | int f_flags; | 1673 | int f_flags; |
1557 | 1674 | ||
1558 | if (file) | 1675 | if (file) |
@@ -1560,12 +1677,19 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, | |||
1560 | else | 1677 | else |
1561 | f_flags = substream->f_flags; | 1678 | f_flags = substream->f_flags; |
1562 | 1679 | ||
1563 | snd_power_lock(card); | 1680 | snd_pcm_stream_lock_irq(substream); |
1564 | if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) | 1681 | switch (substream->runtime->status->state) { |
1565 | res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, | 1682 | case SNDRV_PCM_STATE_PAUSED: |
1566 | substream, f_flags); | 1683 | snd_pcm_pause(substream, 0); |
1567 | snd_power_unlock(card); | 1684 | /* fallthru */ |
1568 | return res; | 1685 | case SNDRV_PCM_STATE_SUSPENDED: |
1686 | snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); | ||
1687 | break; | ||
1688 | } | ||
1689 | snd_pcm_stream_unlock_irq(substream); | ||
1690 | |||
1691 | return snd_pcm_action_nonatomic(&snd_pcm_action_prepare, | ||
1692 | substream, f_flags); | ||
1569 | } | 1693 | } |
1570 | 1694 | ||
1571 | /* | 1695 | /* |
@@ -1662,15 +1786,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, | |||
1662 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) | 1786 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) |
1663 | return -EBADFD; | 1787 | return -EBADFD; |
1664 | 1788 | ||
1665 | snd_power_lock(card); | ||
1666 | if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { | ||
1667 | result = snd_power_wait(card, SNDRV_CTL_POWER_D0); | ||
1668 | if (result < 0) { | ||
1669 | snd_power_unlock(card); | ||
1670 | return result; | ||
1671 | } | ||
1672 | } | ||
1673 | |||
1674 | if (file) { | 1789 | if (file) { |
1675 | if (file->f_flags & O_NONBLOCK) | 1790 | if (file->f_flags & O_NONBLOCK) |
1676 | nonblock = 1; | 1791 | nonblock = 1; |
@@ -1753,7 +1868,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, | |||
1753 | unlock: | 1868 | unlock: |
1754 | snd_pcm_stream_unlock_irq(substream); | 1869 | snd_pcm_stream_unlock_irq(substream); |
1755 | up_read(&snd_pcm_link_rwsem); | 1870 | up_read(&snd_pcm_link_rwsem); |
1756 | snd_power_unlock(card); | ||
1757 | 1871 | ||
1758 | return result; | 1872 | return result; |
1759 | } | 1873 | } |
@@ -1773,8 +1887,7 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) | |||
1773 | runtime = substream->runtime; | 1887 | runtime = substream->runtime; |
1774 | 1888 | ||
1775 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN || | 1889 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN || |
1776 | runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED || | 1890 | runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) |
1777 | runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) | ||
1778 | return -EBADFD; | 1891 | return -EBADFD; |
1779 | 1892 | ||
1780 | snd_pcm_stream_lock_irq(substream); | 1893 | snd_pcm_stream_lock_irq(substream); |
@@ -1940,7 +2053,8 @@ static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, | |||
1940 | struct snd_pcm_hw_rule *rule) | 2053 | struct snd_pcm_hw_rule *rule) |
1941 | { | 2054 | { |
1942 | unsigned int k; | 2055 | unsigned int k; |
1943 | struct snd_interval *i = hw_param_interval(params, rule->deps[0]); | 2056 | const struct snd_interval *i = |
2057 | hw_param_interval_c(params, rule->deps[0]); | ||
1944 | struct snd_mask m; | 2058 | struct snd_mask m; |
1945 | struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); | 2059 | struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); |
1946 | snd_mask_any(&m); | 2060 | snd_mask_any(&m); |
@@ -1986,8 +2100,10 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, | |||
1986 | #error "Change this table" | 2100 | #error "Change this table" |
1987 | #endif | 2101 | #endif |
1988 | 2102 | ||
1989 | static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, | 2103 | static const unsigned int rates[] = { |
1990 | 48000, 64000, 88200, 96000, 176400, 192000 }; | 2104 | 5512, 8000, 11025, 16000, 22050, 32000, 44100, |
2105 | 48000, 64000, 88200, 96000, 176400, 192000 | ||
2106 | }; | ||
1991 | 2107 | ||
1992 | const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { | 2108 | const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { |
1993 | .count = ARRAY_SIZE(rates), | 2109 | .count = ARRAY_SIZE(rates), |
@@ -2250,7 +2366,6 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) | |||
2250 | } | 2366 | } |
2251 | snd_pcm_detach_substream(substream); | 2367 | snd_pcm_detach_substream(substream); |
2252 | } | 2368 | } |
2253 | |||
2254 | EXPORT_SYMBOL(snd_pcm_release_substream); | 2369 | EXPORT_SYMBOL(snd_pcm_release_substream); |
2255 | 2370 | ||
2256 | int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, | 2371 | int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, |
@@ -2292,7 +2407,6 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, | |||
2292 | snd_pcm_release_substream(substream); | 2407 | snd_pcm_release_substream(substream); |
2293 | return err; | 2408 | return err; |
2294 | } | 2409 | } |
2295 | |||
2296 | EXPORT_SYMBOL(snd_pcm_open_substream); | 2410 | EXPORT_SYMBOL(snd_pcm_open_substream); |
2297 | 2411 | ||
2298 | static int snd_pcm_open_file(struct file *file, | 2412 | static int snd_pcm_open_file(struct file *file, |
@@ -2428,50 +2542,84 @@ static int snd_pcm_release(struct inode *inode, struct file *file) | |||
2428 | return 0; | 2542 | return 0; |
2429 | } | 2543 | } |
2430 | 2544 | ||
2431 | static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream, | 2545 | /* check and update PCM state; return 0 or a negative error |
2432 | snd_pcm_uframes_t frames) | 2546 | * call this inside PCM lock |
2547 | */ | ||
2548 | static int do_pcm_hwsync(struct snd_pcm_substream *substream) | ||
2433 | { | 2549 | { |
2434 | struct snd_pcm_runtime *runtime = substream->runtime; | 2550 | switch (substream->runtime->status->state) { |
2435 | snd_pcm_sframes_t appl_ptr; | ||
2436 | snd_pcm_sframes_t ret; | ||
2437 | snd_pcm_sframes_t hw_avail; | ||
2438 | |||
2439 | if (frames == 0) | ||
2440 | return 0; | ||
2441 | |||
2442 | snd_pcm_stream_lock_irq(substream); | ||
2443 | switch (runtime->status->state) { | ||
2444 | case SNDRV_PCM_STATE_PREPARED: | ||
2445 | break; | ||
2446 | case SNDRV_PCM_STATE_DRAINING: | 2551 | case SNDRV_PCM_STATE_DRAINING: |
2447 | case SNDRV_PCM_STATE_RUNNING: | 2552 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
2448 | if (snd_pcm_update_hw_ptr(substream) >= 0) | 2553 | return -EBADFD; |
2449 | break; | ||
2450 | /* Fall through */ | 2554 | /* Fall through */ |
2451 | case SNDRV_PCM_STATE_XRUN: | 2555 | case SNDRV_PCM_STATE_RUNNING: |
2452 | ret = -EPIPE; | 2556 | return snd_pcm_update_hw_ptr(substream); |
2453 | goto __end; | 2557 | case SNDRV_PCM_STATE_PREPARED: |
2558 | case SNDRV_PCM_STATE_PAUSED: | ||
2559 | return 0; | ||
2454 | case SNDRV_PCM_STATE_SUSPENDED: | 2560 | case SNDRV_PCM_STATE_SUSPENDED: |
2455 | ret = -ESTRPIPE; | 2561 | return -ESTRPIPE; |
2456 | goto __end; | 2562 | case SNDRV_PCM_STATE_XRUN: |
2563 | return -EPIPE; | ||
2457 | default: | 2564 | default: |
2458 | ret = -EBADFD; | 2565 | return -EBADFD; |
2459 | goto __end; | ||
2460 | } | 2566 | } |
2567 | } | ||
2461 | 2568 | ||
2462 | hw_avail = snd_pcm_playback_hw_avail(runtime); | 2569 | /* increase the appl_ptr; returns the processed frames or a negative error */ |
2463 | if (hw_avail <= 0) { | 2570 | static snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream, |
2464 | ret = 0; | 2571 | snd_pcm_uframes_t frames, |
2465 | goto __end; | 2572 | snd_pcm_sframes_t avail) |
2466 | } | 2573 | { |
2467 | if (frames > (snd_pcm_uframes_t)hw_avail) | 2574 | struct snd_pcm_runtime *runtime = substream->runtime; |
2468 | frames = hw_avail; | 2575 | snd_pcm_sframes_t appl_ptr; |
2576 | int ret; | ||
2577 | |||
2578 | if (avail <= 0) | ||
2579 | return 0; | ||
2580 | if (frames > (snd_pcm_uframes_t)avail) | ||
2581 | frames = avail; | ||
2582 | appl_ptr = runtime->control->appl_ptr + frames; | ||
2583 | if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) | ||
2584 | appl_ptr -= runtime->boundary; | ||
2585 | ret = pcm_lib_apply_appl_ptr(substream, appl_ptr); | ||
2586 | return ret < 0 ? ret : frames; | ||
2587 | } | ||
2588 | |||
2589 | /* decrease the appl_ptr; returns the processed frames or a negative error */ | ||
2590 | static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream, | ||
2591 | snd_pcm_uframes_t frames, | ||
2592 | snd_pcm_sframes_t avail) | ||
2593 | { | ||
2594 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
2595 | snd_pcm_sframes_t appl_ptr; | ||
2596 | int ret; | ||
2597 | |||
2598 | if (avail <= 0) | ||
2599 | return 0; | ||
2600 | if (frames > (snd_pcm_uframes_t)avail) | ||
2601 | frames = avail; | ||
2469 | appl_ptr = runtime->control->appl_ptr - frames; | 2602 | appl_ptr = runtime->control->appl_ptr - frames; |
2470 | if (appl_ptr < 0) | 2603 | if (appl_ptr < 0) |
2471 | appl_ptr += runtime->boundary; | 2604 | appl_ptr += runtime->boundary; |
2472 | runtime->control->appl_ptr = appl_ptr; | 2605 | ret = pcm_lib_apply_appl_ptr(substream, appl_ptr); |
2473 | ret = frames; | 2606 | return ret < 0 ? ret : frames; |
2474 | __end: | 2607 | } |
2608 | |||
2609 | static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream, | ||
2610 | snd_pcm_uframes_t frames) | ||
2611 | { | ||
2612 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
2613 | snd_pcm_sframes_t ret; | ||
2614 | |||
2615 | if (frames == 0) | ||
2616 | return 0; | ||
2617 | |||
2618 | snd_pcm_stream_lock_irq(substream); | ||
2619 | ret = do_pcm_hwsync(substream); | ||
2620 | if (!ret) | ||
2621 | ret = rewind_appl_ptr(substream, frames, | ||
2622 | snd_pcm_playback_hw_avail(runtime)); | ||
2475 | snd_pcm_stream_unlock_irq(substream); | 2623 | snd_pcm_stream_unlock_irq(substream); |
2476 | return ret; | 2624 | return ret; |
2477 | } | 2625 | } |
@@ -2480,46 +2628,16 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr | |||
2480 | snd_pcm_uframes_t frames) | 2628 | snd_pcm_uframes_t frames) |
2481 | { | 2629 | { |
2482 | struct snd_pcm_runtime *runtime = substream->runtime; | 2630 | struct snd_pcm_runtime *runtime = substream->runtime; |
2483 | snd_pcm_sframes_t appl_ptr; | ||
2484 | snd_pcm_sframes_t ret; | 2631 | snd_pcm_sframes_t ret; |
2485 | snd_pcm_sframes_t hw_avail; | ||
2486 | 2632 | ||
2487 | if (frames == 0) | 2633 | if (frames == 0) |
2488 | return 0; | 2634 | return 0; |
2489 | 2635 | ||
2490 | snd_pcm_stream_lock_irq(substream); | 2636 | snd_pcm_stream_lock_irq(substream); |
2491 | switch (runtime->status->state) { | 2637 | ret = do_pcm_hwsync(substream); |
2492 | case SNDRV_PCM_STATE_PREPARED: | 2638 | if (!ret) |
2493 | case SNDRV_PCM_STATE_DRAINING: | 2639 | ret = rewind_appl_ptr(substream, frames, |
2494 | break; | 2640 | snd_pcm_capture_hw_avail(runtime)); |
2495 | case SNDRV_PCM_STATE_RUNNING: | ||
2496 | if (snd_pcm_update_hw_ptr(substream) >= 0) | ||
2497 | break; | ||
2498 | /* Fall through */ | ||
2499 | case SNDRV_PCM_STATE_XRUN: | ||
2500 | ret = -EPIPE; | ||
2501 | goto __end; | ||
2502 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2503 | ret = -ESTRPIPE; | ||
2504 | goto __end; | ||
2505 | default: | ||
2506 | ret = -EBADFD; | ||
2507 | goto __end; | ||
2508 | } | ||
2509 | |||
2510 | hw_avail = snd_pcm_capture_hw_avail(runtime); | ||
2511 | if (hw_avail <= 0) { | ||
2512 | ret = 0; | ||
2513 | goto __end; | ||
2514 | } | ||
2515 | if (frames > (snd_pcm_uframes_t)hw_avail) | ||
2516 | frames = hw_avail; | ||
2517 | appl_ptr = runtime->control->appl_ptr - frames; | ||
2518 | if (appl_ptr < 0) | ||
2519 | appl_ptr += runtime->boundary; | ||
2520 | runtime->control->appl_ptr = appl_ptr; | ||
2521 | ret = frames; | ||
2522 | __end: | ||
2523 | snd_pcm_stream_unlock_irq(substream); | 2641 | snd_pcm_stream_unlock_irq(substream); |
2524 | return ret; | 2642 | return ret; |
2525 | } | 2643 | } |
@@ -2528,47 +2646,16 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs | |||
2528 | snd_pcm_uframes_t frames) | 2646 | snd_pcm_uframes_t frames) |
2529 | { | 2647 | { |
2530 | struct snd_pcm_runtime *runtime = substream->runtime; | 2648 | struct snd_pcm_runtime *runtime = substream->runtime; |
2531 | snd_pcm_sframes_t appl_ptr; | ||
2532 | snd_pcm_sframes_t ret; | 2649 | snd_pcm_sframes_t ret; |
2533 | snd_pcm_sframes_t avail; | ||
2534 | 2650 | ||
2535 | if (frames == 0) | 2651 | if (frames == 0) |
2536 | return 0; | 2652 | return 0; |
2537 | 2653 | ||
2538 | snd_pcm_stream_lock_irq(substream); | 2654 | snd_pcm_stream_lock_irq(substream); |
2539 | switch (runtime->status->state) { | 2655 | ret = do_pcm_hwsync(substream); |
2540 | case SNDRV_PCM_STATE_PREPARED: | 2656 | if (!ret) |
2541 | case SNDRV_PCM_STATE_PAUSED: | 2657 | ret = forward_appl_ptr(substream, frames, |
2542 | break; | 2658 | snd_pcm_playback_avail(runtime)); |
2543 | case SNDRV_PCM_STATE_DRAINING: | ||
2544 | case SNDRV_PCM_STATE_RUNNING: | ||
2545 | if (snd_pcm_update_hw_ptr(substream) >= 0) | ||
2546 | break; | ||
2547 | /* Fall through */ | ||
2548 | case SNDRV_PCM_STATE_XRUN: | ||
2549 | ret = -EPIPE; | ||
2550 | goto __end; | ||
2551 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2552 | ret = -ESTRPIPE; | ||
2553 | goto __end; | ||
2554 | default: | ||
2555 | ret = -EBADFD; | ||
2556 | goto __end; | ||
2557 | } | ||
2558 | |||
2559 | avail = snd_pcm_playback_avail(runtime); | ||
2560 | if (avail <= 0) { | ||
2561 | ret = 0; | ||
2562 | goto __end; | ||
2563 | } | ||
2564 | if (frames > (snd_pcm_uframes_t)avail) | ||
2565 | frames = avail; | ||
2566 | appl_ptr = runtime->control->appl_ptr + frames; | ||
2567 | if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) | ||
2568 | appl_ptr -= runtime->boundary; | ||
2569 | runtime->control->appl_ptr = appl_ptr; | ||
2570 | ret = frames; | ||
2571 | __end: | ||
2572 | snd_pcm_stream_unlock_irq(substream); | 2659 | snd_pcm_stream_unlock_irq(substream); |
2573 | return ret; | 2660 | return ret; |
2574 | } | 2661 | } |
@@ -2577,123 +2664,47 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst | |||
2577 | snd_pcm_uframes_t frames) | 2664 | snd_pcm_uframes_t frames) |
2578 | { | 2665 | { |
2579 | struct snd_pcm_runtime *runtime = substream->runtime; | 2666 | struct snd_pcm_runtime *runtime = substream->runtime; |
2580 | snd_pcm_sframes_t appl_ptr; | ||
2581 | snd_pcm_sframes_t ret; | 2667 | snd_pcm_sframes_t ret; |
2582 | snd_pcm_sframes_t avail; | ||
2583 | 2668 | ||
2584 | if (frames == 0) | 2669 | if (frames == 0) |
2585 | return 0; | 2670 | return 0; |
2586 | 2671 | ||
2587 | snd_pcm_stream_lock_irq(substream); | 2672 | snd_pcm_stream_lock_irq(substream); |
2588 | switch (runtime->status->state) { | 2673 | ret = do_pcm_hwsync(substream); |
2589 | case SNDRV_PCM_STATE_PREPARED: | 2674 | if (!ret) |
2590 | case SNDRV_PCM_STATE_DRAINING: | 2675 | ret = forward_appl_ptr(substream, frames, |
2591 | case SNDRV_PCM_STATE_PAUSED: | 2676 | snd_pcm_capture_avail(runtime)); |
2592 | break; | ||
2593 | case SNDRV_PCM_STATE_RUNNING: | ||
2594 | if (snd_pcm_update_hw_ptr(substream) >= 0) | ||
2595 | break; | ||
2596 | /* Fall through */ | ||
2597 | case SNDRV_PCM_STATE_XRUN: | ||
2598 | ret = -EPIPE; | ||
2599 | goto __end; | ||
2600 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2601 | ret = -ESTRPIPE; | ||
2602 | goto __end; | ||
2603 | default: | ||
2604 | ret = -EBADFD; | ||
2605 | goto __end; | ||
2606 | } | ||
2607 | |||
2608 | avail = snd_pcm_capture_avail(runtime); | ||
2609 | if (avail <= 0) { | ||
2610 | ret = 0; | ||
2611 | goto __end; | ||
2612 | } | ||
2613 | if (frames > (snd_pcm_uframes_t)avail) | ||
2614 | frames = avail; | ||
2615 | appl_ptr = runtime->control->appl_ptr + frames; | ||
2616 | if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) | ||
2617 | appl_ptr -= runtime->boundary; | ||
2618 | runtime->control->appl_ptr = appl_ptr; | ||
2619 | ret = frames; | ||
2620 | __end: | ||
2621 | snd_pcm_stream_unlock_irq(substream); | 2677 | snd_pcm_stream_unlock_irq(substream); |
2622 | return ret; | 2678 | return ret; |
2623 | } | 2679 | } |
2624 | 2680 | ||
2625 | static int snd_pcm_hwsync(struct snd_pcm_substream *substream) | 2681 | static int snd_pcm_hwsync(struct snd_pcm_substream *substream) |
2626 | { | 2682 | { |
2627 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
2628 | int err; | 2683 | int err; |
2629 | 2684 | ||
2630 | snd_pcm_stream_lock_irq(substream); | 2685 | snd_pcm_stream_lock_irq(substream); |
2631 | switch (runtime->status->state) { | 2686 | err = do_pcm_hwsync(substream); |
2632 | case SNDRV_PCM_STATE_DRAINING: | ||
2633 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
2634 | goto __badfd; | ||
2635 | /* Fall through */ | ||
2636 | case SNDRV_PCM_STATE_RUNNING: | ||
2637 | if ((err = snd_pcm_update_hw_ptr(substream)) < 0) | ||
2638 | break; | ||
2639 | /* Fall through */ | ||
2640 | case SNDRV_PCM_STATE_PREPARED: | ||
2641 | err = 0; | ||
2642 | break; | ||
2643 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2644 | err = -ESTRPIPE; | ||
2645 | break; | ||
2646 | case SNDRV_PCM_STATE_XRUN: | ||
2647 | err = -EPIPE; | ||
2648 | break; | ||
2649 | default: | ||
2650 | __badfd: | ||
2651 | err = -EBADFD; | ||
2652 | break; | ||
2653 | } | ||
2654 | snd_pcm_stream_unlock_irq(substream); | 2687 | snd_pcm_stream_unlock_irq(substream); |
2655 | return err; | 2688 | return err; |
2656 | } | 2689 | } |
2657 | 2690 | ||
2658 | static int snd_pcm_delay(struct snd_pcm_substream *substream, | 2691 | static snd_pcm_sframes_t snd_pcm_delay(struct snd_pcm_substream *substream) |
2659 | snd_pcm_sframes_t __user *res) | ||
2660 | { | 2692 | { |
2661 | struct snd_pcm_runtime *runtime = substream->runtime; | 2693 | struct snd_pcm_runtime *runtime = substream->runtime; |
2662 | int err; | 2694 | int err; |
2663 | snd_pcm_sframes_t n = 0; | 2695 | snd_pcm_sframes_t n = 0; |
2664 | 2696 | ||
2665 | snd_pcm_stream_lock_irq(substream); | 2697 | snd_pcm_stream_lock_irq(substream); |
2666 | switch (runtime->status->state) { | 2698 | err = do_pcm_hwsync(substream); |
2667 | case SNDRV_PCM_STATE_DRAINING: | 2699 | if (!err) { |
2668 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
2669 | goto __badfd; | ||
2670 | /* Fall through */ | ||
2671 | case SNDRV_PCM_STATE_RUNNING: | ||
2672 | if ((err = snd_pcm_update_hw_ptr(substream)) < 0) | ||
2673 | break; | ||
2674 | /* Fall through */ | ||
2675 | case SNDRV_PCM_STATE_PREPARED: | ||
2676 | case SNDRV_PCM_STATE_SUSPENDED: | ||
2677 | err = 0; | ||
2678 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 2700 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
2679 | n = snd_pcm_playback_hw_avail(runtime); | 2701 | n = snd_pcm_playback_hw_avail(runtime); |
2680 | else | 2702 | else |
2681 | n = snd_pcm_capture_avail(runtime); | 2703 | n = snd_pcm_capture_avail(runtime); |
2682 | n += runtime->delay; | 2704 | n += runtime->delay; |
2683 | break; | ||
2684 | case SNDRV_PCM_STATE_XRUN: | ||
2685 | err = -EPIPE; | ||
2686 | break; | ||
2687 | default: | ||
2688 | __badfd: | ||
2689 | err = -EBADFD; | ||
2690 | break; | ||
2691 | } | 2705 | } |
2692 | snd_pcm_stream_unlock_irq(substream); | 2706 | snd_pcm_stream_unlock_irq(substream); |
2693 | if (!err) | 2707 | return err < 0 ? err : n; |
2694 | if (put_user(n, res)) | ||
2695 | err = -EFAULT; | ||
2696 | return err; | ||
2697 | } | 2708 | } |
2698 | 2709 | ||
2699 | static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, | 2710 | static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, |
@@ -2718,10 +2729,16 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, | |||
2718 | return err; | 2729 | return err; |
2719 | } | 2730 | } |
2720 | snd_pcm_stream_lock_irq(substream); | 2731 | snd_pcm_stream_lock_irq(substream); |
2721 | if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) | 2732 | if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { |
2722 | control->appl_ptr = sync_ptr.c.control.appl_ptr; | 2733 | err = pcm_lib_apply_appl_ptr(substream, |
2723 | else | 2734 | sync_ptr.c.control.appl_ptr); |
2735 | if (err < 0) { | ||
2736 | snd_pcm_stream_unlock_irq(substream); | ||
2737 | return err; | ||
2738 | } | ||
2739 | } else { | ||
2724 | sync_ptr.c.control.appl_ptr = control->appl_ptr; | 2740 | sync_ptr.c.control.appl_ptr = control->appl_ptr; |
2741 | } | ||
2725 | if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) | 2742 | if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) |
2726 | control->avail_min = sync_ptr.c.control.avail_min; | 2743 | control->avail_min = sync_ptr.c.control.avail_min; |
2727 | else | 2744 | else |
@@ -2749,10 +2766,12 @@ static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg) | |||
2749 | return 0; | 2766 | return 0; |
2750 | } | 2767 | } |
2751 | 2768 | ||
2752 | static int snd_pcm_common_ioctl1(struct file *file, | 2769 | static int snd_pcm_common_ioctl(struct file *file, |
2753 | struct snd_pcm_substream *substream, | 2770 | struct snd_pcm_substream *substream, |
2754 | unsigned int cmd, void __user *arg) | 2771 | unsigned int cmd, void __user *arg) |
2755 | { | 2772 | { |
2773 | struct snd_pcm_file *pcm_file = file->private_data; | ||
2774 | |||
2756 | switch (cmd) { | 2775 | switch (cmd) { |
2757 | case SNDRV_PCM_IOCTL_PVERSION: | 2776 | case SNDRV_PCM_IOCTL_PVERSION: |
2758 | return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; | 2777 | return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; |
@@ -2762,6 +2781,11 @@ static int snd_pcm_common_ioctl1(struct file *file, | |||
2762 | return 0; | 2781 | return 0; |
2763 | case SNDRV_PCM_IOCTL_TTSTAMP: | 2782 | case SNDRV_PCM_IOCTL_TTSTAMP: |
2764 | return snd_pcm_tstamp(substream, arg); | 2783 | return snd_pcm_tstamp(substream, arg); |
2784 | case SNDRV_PCM_IOCTL_USER_PVERSION: | ||
2785 | if (get_user(pcm_file->user_pversion, | ||
2786 | (unsigned int __user *)arg)) | ||
2787 | return -EFAULT; | ||
2788 | return 0; | ||
2765 | case SNDRV_PCM_IOCTL_HW_REFINE: | 2789 | case SNDRV_PCM_IOCTL_HW_REFINE: |
2766 | return snd_pcm_hw_refine_user(substream, arg); | 2790 | return snd_pcm_hw_refine_user(substream, arg); |
2767 | case SNDRV_PCM_IOCTL_HW_PARAMS: | 2791 | case SNDRV_PCM_IOCTL_HW_PARAMS: |
@@ -2781,7 +2805,7 @@ static int snd_pcm_common_ioctl1(struct file *file, | |||
2781 | case SNDRV_PCM_IOCTL_RESET: | 2805 | case SNDRV_PCM_IOCTL_RESET: |
2782 | return snd_pcm_reset(substream); | 2806 | return snd_pcm_reset(substream); |
2783 | case SNDRV_PCM_IOCTL_START: | 2807 | case SNDRV_PCM_IOCTL_START: |
2784 | return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING); | 2808 | return snd_pcm_start_lock_irq(substream); |
2785 | case SNDRV_PCM_IOCTL_LINK: | 2809 | case SNDRV_PCM_IOCTL_LINK: |
2786 | return snd_pcm_link(substream, (int)(unsigned long) arg); | 2810 | return snd_pcm_link(substream, (int)(unsigned long) arg); |
2787 | case SNDRV_PCM_IOCTL_UNLINK: | 2811 | case SNDRV_PCM_IOCTL_UNLINK: |
@@ -2793,7 +2817,16 @@ static int snd_pcm_common_ioctl1(struct file *file, | |||
2793 | case SNDRV_PCM_IOCTL_HWSYNC: | 2817 | case SNDRV_PCM_IOCTL_HWSYNC: |
2794 | return snd_pcm_hwsync(substream); | 2818 | return snd_pcm_hwsync(substream); |
2795 | case SNDRV_PCM_IOCTL_DELAY: | 2819 | case SNDRV_PCM_IOCTL_DELAY: |
2796 | return snd_pcm_delay(substream, arg); | 2820 | { |
2821 | snd_pcm_sframes_t delay = snd_pcm_delay(substream); | ||
2822 | snd_pcm_sframes_t __user *res = arg; | ||
2823 | |||
2824 | if (delay < 0) | ||
2825 | return delay; | ||
2826 | if (put_user(delay, res)) | ||
2827 | return -EFAULT; | ||
2828 | return 0; | ||
2829 | } | ||
2797 | case SNDRV_PCM_IOCTL_SYNC_PTR: | 2830 | case SNDRV_PCM_IOCTL_SYNC_PTR: |
2798 | return snd_pcm_sync_ptr(substream, arg); | 2831 | return snd_pcm_sync_ptr(substream, arg); |
2799 | #ifdef CONFIG_SND_SUPPORT_OLD_API | 2832 | #ifdef CONFIG_SND_SUPPORT_OLD_API |
@@ -2807,23 +2840,34 @@ static int snd_pcm_common_ioctl1(struct file *file, | |||
2807 | case SNDRV_PCM_IOCTL_DROP: | 2840 | case SNDRV_PCM_IOCTL_DROP: |
2808 | return snd_pcm_drop(substream); | 2841 | return snd_pcm_drop(substream); |
2809 | case SNDRV_PCM_IOCTL_PAUSE: | 2842 | case SNDRV_PCM_IOCTL_PAUSE: |
2810 | { | 2843 | return snd_pcm_action_lock_irq(&snd_pcm_action_pause, |
2811 | int res; | 2844 | substream, |
2812 | snd_pcm_stream_lock_irq(substream); | 2845 | (int)(unsigned long)arg); |
2813 | res = snd_pcm_pause(substream, (int)(unsigned long)arg); | ||
2814 | snd_pcm_stream_unlock_irq(substream); | ||
2815 | return res; | ||
2816 | } | ||
2817 | } | 2846 | } |
2818 | pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd); | 2847 | pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd); |
2819 | return -ENOTTY; | 2848 | return -ENOTTY; |
2820 | } | 2849 | } |
2821 | 2850 | ||
2851 | static int snd_pcm_common_ioctl1(struct file *file, | ||
2852 | struct snd_pcm_substream *substream, | ||
2853 | unsigned int cmd, void __user *arg) | ||
2854 | { | ||
2855 | struct snd_card *card = substream->pcm->card; | ||
2856 | int res; | ||
2857 | |||
2858 | snd_power_lock(card); | ||
2859 | res = snd_power_wait(card, SNDRV_CTL_POWER_D0); | ||
2860 | if (res >= 0) | ||
2861 | res = snd_pcm_common_ioctl(file, substream, cmd, arg); | ||
2862 | snd_power_unlock(card); | ||
2863 | return res; | ||
2864 | } | ||
2865 | |||
2822 | static int snd_pcm_playback_ioctl1(struct file *file, | 2866 | static int snd_pcm_playback_ioctl1(struct file *file, |
2823 | struct snd_pcm_substream *substream, | 2867 | struct snd_pcm_substream *substream, |
2824 | unsigned int cmd, void __user *arg) | 2868 | unsigned int cmd, void __user *arg) |
2825 | { | 2869 | { |
2826 | if (snd_BUG_ON(!substream)) | 2870 | if (PCM_RUNTIME_CHECK(substream)) |
2827 | return -ENXIO; | 2871 | return -ENXIO; |
2828 | if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) | 2872 | if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) |
2829 | return -EINVAL; | 2873 | return -EINVAL; |
@@ -2903,7 +2947,7 @@ static int snd_pcm_capture_ioctl1(struct file *file, | |||
2903 | struct snd_pcm_substream *substream, | 2947 | struct snd_pcm_substream *substream, |
2904 | unsigned int cmd, void __user *arg) | 2948 | unsigned int cmd, void __user *arg) |
2905 | { | 2949 | { |
2906 | if (snd_BUG_ON(!substream)) | 2950 | if (PCM_RUNTIME_CHECK(substream)) |
2907 | return -ENXIO; | 2951 | return -ENXIO; |
2908 | if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE)) | 2952 | if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE)) |
2909 | return -EINVAL; | 2953 | return -EINVAL; |
@@ -3007,30 +3051,55 @@ static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd, | |||
3007 | (void __user *)arg); | 3051 | (void __user *)arg); |
3008 | } | 3052 | } |
3009 | 3053 | ||
3054 | /** | ||
3055 | * snd_pcm_kernel_ioctl - Execute PCM ioctl in the kernel-space | ||
3056 | * @substream: PCM substream | ||
3057 | * @cmd: IOCTL cmd | ||
3058 | * @arg: IOCTL argument | ||
3059 | * | ||
3060 | * The function is provided primarily for OSS layer and USB gadget drivers, | ||
3061 | * and it allows only the limited set of ioctls (hw_params, sw_params, | ||
3062 | * prepare, start, drain, drop, forward). | ||
3063 | */ | ||
3010 | int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, | 3064 | int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, |
3011 | unsigned int cmd, void *arg) | 3065 | unsigned int cmd, void *arg) |
3012 | { | 3066 | { |
3013 | mm_segment_t fs; | 3067 | snd_pcm_uframes_t *frames = arg; |
3014 | int result; | 3068 | snd_pcm_sframes_t result; |
3015 | 3069 | ||
3016 | fs = snd_enter_user(); | 3070 | switch (cmd) { |
3017 | switch (substream->stream) { | 3071 | case SNDRV_PCM_IOCTL_FORWARD: |
3018 | case SNDRV_PCM_STREAM_PLAYBACK: | 3072 | { |
3019 | result = snd_pcm_playback_ioctl1(NULL, substream, cmd, | 3073 | /* provided only for OSS; capture-only and no value returned */ |
3020 | (void __user *)arg); | 3074 | if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) |
3021 | break; | 3075 | return -EINVAL; |
3022 | case SNDRV_PCM_STREAM_CAPTURE: | 3076 | result = snd_pcm_capture_forward(substream, *frames); |
3023 | result = snd_pcm_capture_ioctl1(NULL, substream, cmd, | 3077 | return result < 0 ? result : 0; |
3024 | (void __user *)arg); | 3078 | } |
3025 | break; | 3079 | case SNDRV_PCM_IOCTL_HW_PARAMS: |
3080 | return snd_pcm_hw_params(substream, arg); | ||
3081 | case SNDRV_PCM_IOCTL_SW_PARAMS: | ||
3082 | return snd_pcm_sw_params(substream, arg); | ||
3083 | case SNDRV_PCM_IOCTL_PREPARE: | ||
3084 | return snd_pcm_prepare(substream, NULL); | ||
3085 | case SNDRV_PCM_IOCTL_START: | ||
3086 | return snd_pcm_start_lock_irq(substream); | ||
3087 | case SNDRV_PCM_IOCTL_DRAIN: | ||
3088 | return snd_pcm_drain(substream, NULL); | ||
3089 | case SNDRV_PCM_IOCTL_DROP: | ||
3090 | return snd_pcm_drop(substream); | ||
3091 | case SNDRV_PCM_IOCTL_DELAY: | ||
3092 | { | ||
3093 | result = snd_pcm_delay(substream); | ||
3094 | if (result < 0) | ||
3095 | return result; | ||
3096 | *frames = result; | ||
3097 | return 0; | ||
3098 | } | ||
3026 | default: | 3099 | default: |
3027 | result = -EINVAL; | 3100 | return -EINVAL; |
3028 | break; | ||
3029 | } | 3101 | } |
3030 | snd_leave_user(fs); | ||
3031 | return result; | ||
3032 | } | 3102 | } |
3033 | |||
3034 | EXPORT_SYMBOL(snd_pcm_kernel_ioctl); | 3103 | EXPORT_SYMBOL(snd_pcm_kernel_ioctl); |
3035 | 3104 | ||
3036 | static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, | 3105 | static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, |
@@ -3314,10 +3383,41 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file | |||
3314 | area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; | 3383 | area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; |
3315 | return 0; | 3384 | return 0; |
3316 | } | 3385 | } |
3386 | |||
3387 | static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file) | ||
3388 | { | ||
3389 | if (pcm_file->no_compat_mmap) | ||
3390 | return false; | ||
3391 | /* See pcm_control_mmap_allowed() below. | ||
3392 | * Since older alsa-lib requires both status and control mmaps to be | ||
3393 | * coupled, we have to disable the status mmap for old alsa-lib, too. | ||
3394 | */ | ||
3395 | if (pcm_file->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 14) && | ||
3396 | (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR)) | ||
3397 | return false; | ||
3398 | return true; | ||
3399 | } | ||
3400 | |||
3401 | static bool pcm_control_mmap_allowed(struct snd_pcm_file *pcm_file) | ||
3402 | { | ||
3403 | if (pcm_file->no_compat_mmap) | ||
3404 | return false; | ||
3405 | /* Disallow the control mmap when SYNC_APPLPTR flag is set; | ||
3406 | * it enforces the user-space to fall back to snd_pcm_sync_ptr(), | ||
3407 | * thus it effectively assures the manual update of appl_ptr. | ||
3408 | */ | ||
3409 | if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR) | ||
3410 | return false; | ||
3411 | return true; | ||
3412 | } | ||
3413 | |||
3317 | #else /* ! coherent mmap */ | 3414 | #else /* ! coherent mmap */ |
3318 | /* | 3415 | /* |
3319 | * don't support mmap for status and control records. | 3416 | * don't support mmap for status and control records. |
3320 | */ | 3417 | */ |
3418 | #define pcm_status_mmap_allowed(pcm_file) false | ||
3419 | #define pcm_control_mmap_allowed(pcm_file) false | ||
3420 | |||
3321 | static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, | 3421 | static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, |
3322 | struct vm_area_struct *area) | 3422 | struct vm_area_struct *area) |
3323 | { | 3423 | { |
@@ -3437,7 +3537,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, | |||
3437 | area->vm_page_prot = pgprot_noncached(area->vm_page_prot); | 3537 | area->vm_page_prot = pgprot_noncached(area->vm_page_prot); |
3438 | return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes); | 3538 | return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes); |
3439 | } | 3539 | } |
3440 | |||
3441 | EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); | 3540 | EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); |
3442 | #endif /* SNDRV_PCM_INFO_MMAP */ | 3541 | #endif /* SNDRV_PCM_INFO_MMAP */ |
3443 | 3542 | ||
@@ -3486,7 +3585,6 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, | |||
3486 | atomic_inc(&substream->mmap_count); | 3585 | atomic_inc(&substream->mmap_count); |
3487 | return err; | 3586 | return err; |
3488 | } | 3587 | } |
3489 | |||
3490 | EXPORT_SYMBOL(snd_pcm_mmap_data); | 3588 | EXPORT_SYMBOL(snd_pcm_mmap_data); |
3491 | 3589 | ||
3492 | static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) | 3590 | static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) |
@@ -3503,11 +3601,11 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) | |||
3503 | offset = area->vm_pgoff << PAGE_SHIFT; | 3601 | offset = area->vm_pgoff << PAGE_SHIFT; |
3504 | switch (offset) { | 3602 | switch (offset) { |
3505 | case SNDRV_PCM_MMAP_OFFSET_STATUS: | 3603 | case SNDRV_PCM_MMAP_OFFSET_STATUS: |
3506 | if (pcm_file->no_compat_mmap) | 3604 | if (!pcm_status_mmap_allowed(pcm_file)) |
3507 | return -ENXIO; | 3605 | return -ENXIO; |
3508 | return snd_pcm_mmap_status(substream, file, area); | 3606 | return snd_pcm_mmap_status(substream, file, area); |
3509 | case SNDRV_PCM_MMAP_OFFSET_CONTROL: | 3607 | case SNDRV_PCM_MMAP_OFFSET_CONTROL: |
3510 | if (pcm_file->no_compat_mmap) | 3608 | if (!pcm_control_mmap_allowed(pcm_file)) |
3511 | return -ENXIO; | 3609 | return -ENXIO; |
3512 | return snd_pcm_mmap_control(substream, file, area); | 3610 | return snd_pcm_mmap_control(substream, file, area); |
3513 | default: | 3611 | default: |
@@ -3603,12 +3701,17 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, | |||
3603 | } | 3701 | } |
3604 | snd_pcm_hw_convert_from_old_params(params, oparams); | 3702 | snd_pcm_hw_convert_from_old_params(params, oparams); |
3605 | err = snd_pcm_hw_refine(substream, params); | 3703 | err = snd_pcm_hw_refine(substream, params); |
3606 | snd_pcm_hw_convert_to_old_params(oparams, params); | 3704 | if (err < 0) |
3607 | if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { | 3705 | goto out_old; |
3608 | if (!err) | ||
3609 | err = -EFAULT; | ||
3610 | } | ||
3611 | 3706 | ||
3707 | err = fixup_unreferenced_params(substream, params); | ||
3708 | if (err < 0) | ||
3709 | goto out_old; | ||
3710 | |||
3711 | snd_pcm_hw_convert_to_old_params(oparams, params); | ||
3712 | if (copy_to_user(_oparams, oparams, sizeof(*oparams))) | ||
3713 | err = -EFAULT; | ||
3714 | out_old: | ||
3612 | kfree(oparams); | 3715 | kfree(oparams); |
3613 | out: | 3716 | out: |
3614 | kfree(params); | 3717 | kfree(params); |
@@ -3631,14 +3734,16 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, | |||
3631 | err = PTR_ERR(oparams); | 3734 | err = PTR_ERR(oparams); |
3632 | goto out; | 3735 | goto out; |
3633 | } | 3736 | } |
3737 | |||
3634 | snd_pcm_hw_convert_from_old_params(params, oparams); | 3738 | snd_pcm_hw_convert_from_old_params(params, oparams); |
3635 | err = snd_pcm_hw_params(substream, params); | 3739 | err = snd_pcm_hw_params(substream, params); |
3636 | snd_pcm_hw_convert_to_old_params(oparams, params); | 3740 | if (err < 0) |
3637 | if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { | 3741 | goto out_old; |
3638 | if (!err) | ||
3639 | err = -EFAULT; | ||
3640 | } | ||
3641 | 3742 | ||
3743 | snd_pcm_hw_convert_to_old_params(oparams, params); | ||
3744 | if (copy_to_user(_oparams, oparams, sizeof(*oparams))) | ||
3745 | err = -EFAULT; | ||
3746 | out_old: | ||
3642 | kfree(oparams); | 3747 | kfree(oparams); |
3643 | out: | 3748 | out: |
3644 | kfree(params); | 3749 | kfree(params); |