diff options
author | Markus Grabner <grabner@icg.tugraz.at> | 2011-12-10 02:12:32 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-12-09 19:26:09 -0800 |
commit | 6b02a17ee5cd5d200dbe4a285a4e750f70884967 (patch) | |
tree | 4f8a0f63d324ddbb284911a47303282ef0ef0c08 /drivers/staging/line6/pcm.c | |
parent | staging: line6: wait for urbs in snd_line6_prepare() (diff) | |
download | linux-dev-6b02a17ee5cd5d200dbe4a285a4e750f70884967.tar.xz linux-dev-6b02a17ee5cd5d200dbe4a285a4e750f70884967.zip |
staging: line6: fixed ALSA/PCM interaction
The PCM subsystem in the Line6 driver is mainly used for PCM playback and
capture by ALSA, but also has other tasks, most notably providing a
low-latency software monitor for devices which don't support hardware
monitoring (e.g., the TonePort series). This patch makes ALSA "play nicely"
with the other components, i.e., prevents it from resetting the isochronous
USB transfer while other PCM tasks (software monitoring) are running.
Signed-off-by: Markus Grabner <grabner@icg.tugraz.at>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/line6/pcm.c')
-rw-r--r-- | drivers/staging/line6/pcm.c | 65 |
1 files changed, 45 insertions, 20 deletions
diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index 68727b2dfb8f..37675e66da81 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -86,17 +86,22 @@ static DEVICE_ATTR(impulse_period, S_IWUSR | S_IRUGO, pcm_get_impulse_period, #endif +static bool test_flags(unsigned long flags0, unsigned long flags1, + unsigned long mask) +{ + return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); +} + int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels) { unsigned long flags_old = __sync_fetch_and_or(&line6pcm->flags, channels); unsigned long flags_new = flags_old | channels; int err = 0; - + line6pcm->prev_fbuf = NULL; - if (((flags_old & MASK_CAPTURE) == 0) && - ((flags_new & MASK_CAPTURE) != 0)) { + if (test_flags(flags_old, flags_new, MASK_CAPTURE)) { /* Waiting for completion of active URBs in the stop handler is a bug, we therefore report an error if capturing is restarted @@ -105,34 +110,47 @@ int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels) if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) return -EBUSY; + if (!(flags_new & MASK_PCM_ALSA_CAPTURE)) { + err = line6_alloc_capture_buffer(line6pcm); + + if (err < 0) + goto pcm_start_error; + } + line6pcm->count_in = 0; line6pcm->prev_fsize = 0; err = line6_submit_audio_in_all_urbs(line6pcm); - if (err < 0) { - __sync_fetch_and_and(&line6pcm->flags, ~channels); - return err; - } + if (err < 0) + goto pcm_start_error; } - if (((flags_old & MASK_PLAYBACK) == 0) && - ((flags_new & MASK_PLAYBACK) != 0)) { + if (test_flags(flags_old, flags_new, MASK_PLAYBACK)) { /* See comment above regarding PCM restart. */ if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) return -EBUSY; + if (!(flags_new & MASK_PCM_ALSA_PLAYBACK)) { + err = line6_alloc_playback_buffer(line6pcm); + + if (err < 0) + goto pcm_start_error; + } + line6pcm->count_out = 0; err = line6_submit_audio_out_all_urbs(line6pcm); - if (err < 0) { - __sync_fetch_and_and(&line6pcm->flags, ~channels); - return err; - } + if (err < 0) + goto pcm_start_error; } return 0; + +pcm_start_error: + __sync_fetch_and_and(&line6pcm->flags, ~channels); + return err; } int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels) @@ -141,14 +159,18 @@ int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels) __sync_fetch_and_and(&line6pcm->flags, ~channels); unsigned long flags_new = flags_old & ~channels; - if (((flags_old & MASK_CAPTURE) != 0) && - ((flags_new & MASK_CAPTURE) == 0)) { + if (test_flags(flags_new, flags_old, MASK_CAPTURE)) { line6_unlink_audio_in_urbs(line6pcm); + + if (!(flags_old & MASK_PCM_ALSA_CAPTURE)) + line6_free_capture_buffer(line6pcm); } - if (((flags_old & MASK_PLAYBACK) != 0) && - ((flags_new & MASK_PLAYBACK) == 0)) { + if (test_flags(flags_new, flags_old, MASK_PLAYBACK)) { line6_unlink_audio_out_urbs(line6pcm); + + if (!(flags_old & MASK_PCM_ALSA_PLAYBACK)) + line6_free_playback_buffer(line6pcm); } return 0; @@ -476,18 +498,21 @@ int snd_line6_prepare(struct snd_pcm_substream *substream) switch (substream->stream) { case SNDRV_PCM_STREAM_PLAYBACK: - line6_unlink_wait_clear_audio_out_urbs(line6pcm); + if ((line6pcm->flags & MASK_PLAYBACK) == 0) + line6_unlink_wait_clear_audio_out_urbs(line6pcm); + break; case SNDRV_PCM_STREAM_CAPTURE: - line6_unlink_wait_clear_audio_in_urbs(line6pcm); + if ((line6pcm->flags & MASK_CAPTURE) == 0) + line6_unlink_wait_clear_audio_in_urbs(line6pcm); + break; default: MISSING_CASE; } - if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) { line6pcm->count_out = 0; line6pcm->pos_out = 0; |