diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-08-20 10:40:16 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-08-20 10:40:16 -0400 |
commit | 4cdc115fd38b54642e8536a5c2389483bcb9b2e9 (patch) | |
tree | e68cfe5e88d155d1a6820b563517dac27b8e6dd6 /sound | |
parent | 64f1607ffbbc772685733ea63e6f7f4183df1b16 (diff) |
ALSA: pcm - Fix drain behavior in non-blocking mode
The current PCM core has the following problems regarding PCM draining
in non-blocking mode:
- the current f_flags isn't checked in snd_pcm_drain(), thus changing
the mode dynamically via snd_pcm_nonblock() after open doesn't work.
- calling drain in non-blocking mode just return -EAGAIN error, but
doesn't provide any way to sync with draining.
This patch fixes these issues.
- check file->f_flags in snd_pcm_drain() properly
- when O_NONBLOCK is set, PCM core sets the stream(s) to DRAIN state
but quits ioctl immediately without waiting the whole drain; the
caller can sync the drain manually via poll()
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/core/pcm_lib.c | 12 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 52 |
2 files changed, 37 insertions, 27 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 72cfd47af6b..e3e78c7f274 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -197,12 +197,16 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, | |||
197 | avail = snd_pcm_capture_avail(runtime); | 197 | avail = snd_pcm_capture_avail(runtime); |
198 | if (avail > runtime->avail_max) | 198 | if (avail > runtime->avail_max) |
199 | runtime->avail_max = avail; | 199 | runtime->avail_max = avail; |
200 | if (avail >= runtime->stop_threshold) { | 200 | if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { |
201 | if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING) | 201 | if (avail >= runtime->buffer_size) { |
202 | snd_pcm_drain_done(substream); | 202 | snd_pcm_drain_done(substream); |
203 | else | 203 | return -EPIPE; |
204 | } | ||
205 | } else { | ||
206 | if (avail >= runtime->stop_threshold) { | ||
204 | xrun(substream); | 207 | xrun(substream); |
205 | return -EPIPE; | 208 | return -EPIPE; |
209 | } | ||
206 | } | 210 | } |
207 | if (avail >= runtime->control->avail_min) | 211 | if (avail >= runtime->control->avail_min) |
208 | wake_up(&runtime->sleep); | 212 | wake_up(&runtime->sleep); |
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index ac2150e0670..b08898cbfed 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c | |||
@@ -1343,8 +1343,6 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, | |||
1343 | 1343 | ||
1344 | static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state) | 1344 | static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state) |
1345 | { | 1345 | { |
1346 | if (substream->f_flags & O_NONBLOCK) | ||
1347 | return -EAGAIN; | ||
1348 | substream->runtime->trigger_master = substream; | 1346 | substream->runtime->trigger_master = substream; |
1349 | return 0; | 1347 | return 0; |
1350 | } | 1348 | } |
@@ -1392,7 +1390,6 @@ static struct action_ops snd_pcm_action_drain_init = { | |||
1392 | struct drain_rec { | 1390 | struct drain_rec { |
1393 | struct snd_pcm_substream *substream; | 1391 | struct snd_pcm_substream *substream; |
1394 | wait_queue_t wait; | 1392 | wait_queue_t wait; |
1395 | snd_pcm_uframes_t stop_threshold; | ||
1396 | }; | 1393 | }; |
1397 | 1394 | ||
1398 | static int snd_pcm_drop(struct snd_pcm_substream *substream); | 1395 | static int snd_pcm_drop(struct snd_pcm_substream *substream); |
@@ -1404,13 +1401,15 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream); | |||
1404 | * After this call, all streams are supposed to be either SETUP or DRAINING | 1401 | * After this call, all streams are supposed to be either SETUP or DRAINING |
1405 | * (capture only) state. | 1402 | * (capture only) state. |
1406 | */ | 1403 | */ |
1407 | static int snd_pcm_drain(struct snd_pcm_substream *substream) | 1404 | static int snd_pcm_drain(struct snd_pcm_substream *substream, |
1405 | struct file *file) | ||
1408 | { | 1406 | { |
1409 | struct snd_card *card; | 1407 | struct snd_card *card; |
1410 | struct snd_pcm_runtime *runtime; | 1408 | struct snd_pcm_runtime *runtime; |
1411 | struct snd_pcm_substream *s; | 1409 | struct snd_pcm_substream *s; |
1412 | int result = 0; | 1410 | int result = 0; |
1413 | int i, num_drecs; | 1411 | int i, num_drecs; |
1412 | int nonblock = 0; | ||
1414 | struct drain_rec *drec, drec_tmp, *d; | 1413 | struct drain_rec *drec, drec_tmp, *d; |
1415 | 1414 | ||
1416 | card = substream->pcm->card; | 1415 | card = substream->pcm->card; |
@@ -1428,6 +1427,15 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) | |||
1428 | } | 1427 | } |
1429 | } | 1428 | } |
1430 | 1429 | ||
1430 | if (file) { | ||
1431 | if (file->f_flags & O_NONBLOCK) | ||
1432 | nonblock = 1; | ||
1433 | } else if (substream->f_flags & O_NONBLOCK) | ||
1434 | nonblock = 1; | ||
1435 | |||
1436 | if (nonblock) | ||
1437 | goto lock; /* no need to allocate waitqueues */ | ||
1438 | |||
1431 | /* allocate temporary record for drain sync */ | 1439 | /* allocate temporary record for drain sync */ |
1432 | down_read(&snd_pcm_link_rwsem); | 1440 | down_read(&snd_pcm_link_rwsem); |
1433 | if (snd_pcm_stream_linked(substream)) { | 1441 | if (snd_pcm_stream_linked(substream)) { |
@@ -1449,16 +1457,11 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) | |||
1449 | d->substream = s; | 1457 | d->substream = s; |
1450 | init_waitqueue_entry(&d->wait, current); | 1458 | init_waitqueue_entry(&d->wait, current); |
1451 | add_wait_queue(&runtime->sleep, &d->wait); | 1459 | add_wait_queue(&runtime->sleep, &d->wait); |
1452 | /* stop_threshold fixup to avoid endless loop when | ||
1453 | * stop_threshold > buffer_size | ||
1454 | */ | ||
1455 | d->stop_threshold = runtime->stop_threshold; | ||
1456 | if (runtime->stop_threshold > runtime->buffer_size) | ||
1457 | runtime->stop_threshold = runtime->buffer_size; | ||
1458 | } | 1460 | } |
1459 | } | 1461 | } |
1460 | up_read(&snd_pcm_link_rwsem); | 1462 | up_read(&snd_pcm_link_rwsem); |
1461 | 1463 | ||
1464 | lock: | ||
1462 | snd_pcm_stream_lock_irq(substream); | 1465 | snd_pcm_stream_lock_irq(substream); |
1463 | /* resume pause */ | 1466 | /* resume pause */ |
1464 | if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED) | 1467 | if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED) |
@@ -1466,9 +1469,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) | |||
1466 | 1469 | ||
1467 | /* pre-start/stop - all running streams are changed to DRAINING state */ | 1470 | /* pre-start/stop - all running streams are changed to DRAINING state */ |
1468 | result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0); | 1471 | result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0); |
1469 | if (result < 0) { | 1472 | if (result < 0) |
1470 | snd_pcm_stream_unlock_irq(substream); | 1473 | goto unlock; |
1471 | goto _error; | 1474 | /* in non-blocking, we don't wait in ioctl but let caller poll */ |
1475 | if (nonblock) { | ||
1476 | result = -EAGAIN; | ||
1477 | goto unlock; | ||
1472 | } | 1478 | } |
1473 | 1479 | ||
1474 | for (;;) { | 1480 | for (;;) { |
@@ -1504,18 +1510,18 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) | |||
1504 | } | 1510 | } |
1505 | } | 1511 | } |
1506 | 1512 | ||
1513 | unlock: | ||
1507 | snd_pcm_stream_unlock_irq(substream); | 1514 | snd_pcm_stream_unlock_irq(substream); |
1508 | 1515 | ||
1509 | _error: | 1516 | if (!nonblock) { |
1510 | for (i = 0; i < num_drecs; i++) { | 1517 | for (i = 0; i < num_drecs; i++) { |
1511 | d = &drec[i]; | 1518 | d = &drec[i]; |
1512 | runtime = d->substream->runtime; | 1519 | runtime = d->substream->runtime; |
1513 | remove_wait_queue(&runtime->sleep, &d->wait); | 1520 | remove_wait_queue(&runtime->sleep, &d->wait); |
1514 | runtime->stop_threshold = d->stop_threshold; | 1521 | } |
1522 | if (drec != &drec_tmp) | ||
1523 | kfree(drec); | ||
1515 | } | 1524 | } |
1516 | |||
1517 | if (drec != &drec_tmp) | ||
1518 | kfree(drec); | ||
1519 | snd_power_unlock(card); | 1525 | snd_power_unlock(card); |
1520 | 1526 | ||
1521 | return result; | 1527 | return result; |
@@ -2544,7 +2550,7 @@ static int snd_pcm_common_ioctl1(struct file *file, | |||
2544 | return snd_pcm_hw_params_old_user(substream, arg); | 2550 | return snd_pcm_hw_params_old_user(substream, arg); |
2545 | #endif | 2551 | #endif |
2546 | case SNDRV_PCM_IOCTL_DRAIN: | 2552 | case SNDRV_PCM_IOCTL_DRAIN: |
2547 | return snd_pcm_drain(substream); | 2553 | return snd_pcm_drain(substream, file); |
2548 | case SNDRV_PCM_IOCTL_DROP: | 2554 | case SNDRV_PCM_IOCTL_DROP: |
2549 | return snd_pcm_drop(substream); | 2555 | return snd_pcm_drop(substream); |
2550 | case SNDRV_PCM_IOCTL_PAUSE: | 2556 | case SNDRV_PCM_IOCTL_PAUSE: |