diff options
author | Jaroslav Kysela <perex@perex.cz> | 2010-10-20 02:27:02 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2010-10-20 03:35:38 -0400 |
commit | dd04bb12d047a4d4461772093472a40dbe171e5f (patch) | |
tree | ebbd1b742531f1a2033f11ee838f8d1ed62278be /sound | |
parent | e74670b6fdc37b15ebee11825849d8983e52a74a (diff) |
ALSA: snd-aloop - fix locking issues (running flag updates)
On SMP machines, the cable->running update must be atomic, otherwise
stream is not started correctly sometimes.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/drivers/aloop.c | 32 |
1 files changed, 19 insertions, 13 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 66786ea6f480..38e8351e935d 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c | |||
@@ -263,13 +263,17 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
263 | return err; | 263 | return err; |
264 | dpcm->last_jiffies = jiffies; | 264 | dpcm->last_jiffies = jiffies; |
265 | dpcm->pcm_rate_shift = 0; | 265 | dpcm->pcm_rate_shift = 0; |
266 | loopback_timer_start(dpcm); | 266 | spin_lock(&cable->lock); |
267 | cable->running |= (1 << substream->stream); | 267 | cable->running |= (1 << substream->stream); |
268 | spin_unlock(&cable->lock); | ||
269 | loopback_timer_start(dpcm); | ||
268 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 270 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
269 | loopback_active_notify(dpcm); | 271 | loopback_active_notify(dpcm); |
270 | break; | 272 | break; |
271 | case SNDRV_PCM_TRIGGER_STOP: | 273 | case SNDRV_PCM_TRIGGER_STOP: |
274 | spin_lock(&cable->lock); | ||
272 | cable->running &= ~(1 << substream->stream); | 275 | cable->running &= ~(1 << substream->stream); |
276 | spin_unlock(&cable->lock); | ||
273 | loopback_timer_stop(dpcm); | 277 | loopback_timer_stop(dpcm); |
274 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 278 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
275 | loopback_active_notify(dpcm); | 279 | loopback_active_notify(dpcm); |
@@ -454,28 +458,30 @@ static void loopback_bytepos_update(struct loopback_pcm *dpcm, | |||
454 | } | 458 | } |
455 | } | 459 | } |
456 | 460 | ||
457 | static void loopback_pos_update(struct loopback_cable *cable) | 461 | static unsigned int loopback_pos_update(struct loopback_cable *cable) |
458 | { | 462 | { |
459 | struct loopback_pcm *dpcm_play = | 463 | struct loopback_pcm *dpcm_play = |
460 | cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; | 464 | cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; |
461 | struct loopback_pcm *dpcm_capt = | 465 | struct loopback_pcm *dpcm_capt = |
462 | cable->streams[SNDRV_PCM_STREAM_CAPTURE]; | 466 | cable->streams[SNDRV_PCM_STREAM_CAPTURE]; |
463 | unsigned long delta_play = 0, delta_capt = 0; | 467 | unsigned long delta_play = 0, delta_capt = 0; |
468 | unsigned int running; | ||
464 | 469 | ||
465 | spin_lock(&cable->lock); | 470 | spin_lock(&cable->lock); |
466 | if (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { | 471 | running = cable->running; |
472 | if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { | ||
467 | delta_play = jiffies - dpcm_play->last_jiffies; | 473 | delta_play = jiffies - dpcm_play->last_jiffies; |
468 | dpcm_play->last_jiffies += delta_play; | 474 | dpcm_play->last_jiffies += delta_play; |
469 | } | 475 | } |
470 | 476 | ||
471 | if (cable->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { | 477 | if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { |
472 | delta_capt = jiffies - dpcm_capt->last_jiffies; | 478 | delta_capt = jiffies - dpcm_capt->last_jiffies; |
473 | dpcm_capt->last_jiffies += delta_capt; | 479 | dpcm_capt->last_jiffies += delta_capt; |
474 | } | 480 | } |
475 | 481 | ||
476 | if (delta_play == 0 && delta_capt == 0) { | 482 | if (delta_play == 0 && delta_capt == 0) { |
477 | spin_unlock(&cable->lock); | 483 | spin_unlock(&cable->lock); |
478 | return; | 484 | return running; |
479 | } | 485 | } |
480 | 486 | ||
481 | if (delta_play > delta_capt) { | 487 | if (delta_play > delta_capt) { |
@@ -490,27 +496,27 @@ static void loopback_pos_update(struct loopback_cable *cable) | |||
490 | 496 | ||
491 | if (delta_play == 0 && delta_capt == 0) { | 497 | if (delta_play == 0 && delta_capt == 0) { |
492 | spin_unlock(&cable->lock); | 498 | spin_unlock(&cable->lock); |
493 | return; | 499 | return running; |
494 | } | 500 | } |
495 | /* note delta_capt == delta_play at this moment */ | 501 | /* note delta_capt == delta_play at this moment */ |
496 | loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); | 502 | loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); |
497 | loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); | 503 | loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); |
498 | spin_unlock(&cable->lock); | 504 | spin_unlock(&cable->lock); |
505 | return running; | ||
499 | } | 506 | } |
500 | 507 | ||
501 | static void loopback_timer_function(unsigned long data) | 508 | static void loopback_timer_function(unsigned long data) |
502 | { | 509 | { |
503 | struct loopback_pcm *dpcm = (struct loopback_pcm *)data; | 510 | struct loopback_pcm *dpcm = (struct loopback_pcm *)data; |
504 | int stream; | 511 | unsigned int running; |
505 | 512 | ||
506 | loopback_pos_update(dpcm->cable); | 513 | running = loopback_pos_update(dpcm->cable); |
507 | stream = dpcm->substream->stream; | 514 | if (running & (1 << dpcm->substream->stream)) { |
508 | if (dpcm->cable->running & (1 << stream)) | ||
509 | loopback_timer_start(dpcm); | 515 | loopback_timer_start(dpcm); |
510 | if (dpcm->period_update_pending) { | 516 | if (dpcm->period_update_pending) { |
511 | dpcm->period_update_pending = 0; | 517 | dpcm->period_update_pending = 0; |
512 | if (dpcm->cable->running & (1 << stream)) | ||
513 | snd_pcm_period_elapsed(dpcm->substream); | 518 | snd_pcm_period_elapsed(dpcm->substream); |
519 | } | ||
514 | } | 520 | } |
515 | } | 521 | } |
516 | 522 | ||