diff options
author | Markus Grabner <grabner@icg.tugraz.at> | 2011-12-09 20:12:32 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-12-09 22:26:09 -0500 |
commit | 6b02a17ee5cd5d200dbe4a285a4e750f70884967 (patch) | |
tree | 4f8a0f63d324ddbb284911a47303282ef0ef0c08 /drivers | |
parent | 665f3f506b1c2684d6f78d6d03c038d1712e561d (diff) |
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')
-rw-r--r-- | drivers/staging/line6/capture.c | 46 | ||||
-rw-r--r-- | drivers/staging/line6/capture.h | 2 | ||||
-rw-r--r-- | drivers/staging/line6/pcm.c | 65 | ||||
-rw-r--r-- | drivers/staging/line6/playback.c | 46 | ||||
-rw-r--r-- | drivers/staging/line6/playback.h | 2 | ||||
-rw-r--r-- | drivers/staging/line6/revision.h | 2 |
6 files changed, 118 insertions, 45 deletions
diff --git a/drivers/staging/line6/capture.c b/drivers/staging/line6/capture.c index 8f59ff3e7a17..127f95247749 100644 --- a/drivers/staging/line6/capture.c +++ b/drivers/staging/line6/capture.c | |||
@@ -193,6 +193,31 @@ void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) | |||
193 | } | 193 | } |
194 | } | 194 | } |
195 | 195 | ||
196 | int line6_alloc_capture_buffer(struct snd_line6_pcm *line6pcm) | ||
197 | { | ||
198 | /* We may be invoked multiple times in a row so allocate once only */ | ||
199 | if (line6pcm->buffer_in) | ||
200 | return 0; | ||
201 | |||
202 | line6pcm->buffer_in = | ||
203 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | ||
204 | line6pcm->max_packet_size, GFP_KERNEL); | ||
205 | |||
206 | if (!line6pcm->buffer_in) { | ||
207 | dev_err(line6pcm->line6->ifcdev, | ||
208 | "cannot malloc capture buffer\n"); | ||
209 | return -ENOMEM; | ||
210 | } | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm) | ||
216 | { | ||
217 | kfree(line6pcm->buffer_in); | ||
218 | line6pcm->buffer_in = NULL; | ||
219 | } | ||
220 | |||
196 | /* | 221 | /* |
197 | * Callback for completed capture URB. | 222 | * Callback for completed capture URB. |
198 | */ | 223 | */ |
@@ -316,16 +341,11 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, | |||
316 | } | 341 | } |
317 | /* -- [FD] end */ | 342 | /* -- [FD] end */ |
318 | 343 | ||
319 | /* We may be invoked multiple times in a row so allocate once only */ | 344 | if ((line6pcm->flags & MASK_CAPTURE) == 0) { |
320 | if (!line6pcm->buffer_in) | 345 | ret = line6_alloc_capture_buffer(line6pcm); |
321 | line6pcm->buffer_in = | ||
322 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | ||
323 | line6pcm->max_packet_size, GFP_KERNEL); | ||
324 | 346 | ||
325 | if (!line6pcm->buffer_in) { | 347 | if (ret < 0) |
326 | dev_err(line6pcm->line6->ifcdev, | 348 | return ret; |
327 | "cannot malloc capture buffer\n"); | ||
328 | return -ENOMEM; | ||
329 | } | 349 | } |
330 | 350 | ||
331 | ret = snd_pcm_lib_malloc_pages(substream, | 351 | ret = snd_pcm_lib_malloc_pages(substream, |
@@ -342,9 +362,11 @@ static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) | |||
342 | { | 362 | { |
343 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | 363 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); |
344 | 364 | ||
345 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); | 365 | if ((line6pcm->flags & MASK_CAPTURE) == 0) { |
346 | kfree(line6pcm->buffer_in); | 366 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); |
347 | line6pcm->buffer_in = NULL; | 367 | line6_free_capture_buffer(line6pcm); |
368 | } | ||
369 | |||
348 | return snd_pcm_lib_free_pages(substream); | 370 | return snd_pcm_lib_free_pages(substream); |
349 | } | 371 | } |
350 | 372 | ||
diff --git a/drivers/staging/line6/capture.h b/drivers/staging/line6/capture.h index a7509fbbb954..366cbaa7c88d 100644 --- a/drivers/staging/line6/capture.h +++ b/drivers/staging/line6/capture.h | |||
@@ -19,11 +19,13 @@ | |||
19 | 19 | ||
20 | extern struct snd_pcm_ops snd_line6_capture_ops; | 20 | extern struct snd_pcm_ops snd_line6_capture_ops; |
21 | 21 | ||
22 | extern int line6_alloc_capture_buffer(struct snd_line6_pcm *line6pcm); | ||
22 | extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, | 23 | extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, |
23 | int fsize); | 24 | int fsize); |
24 | extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm, | 25 | extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm, |
25 | int length); | 26 | int length); |
26 | extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); | 27 | extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); |
28 | extern void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm); | ||
27 | extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); | 29 | extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); |
28 | extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm); | 30 | extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm); |
29 | extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm | 31 | extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm |
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, | |||
86 | 86 | ||
87 | #endif | 87 | #endif |
88 | 88 | ||
89 | static bool test_flags(unsigned long flags0, unsigned long flags1, | ||
90 | unsigned long mask) | ||
91 | { | ||
92 | return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); | ||
93 | } | ||
94 | |||
89 | int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels) | 95 | int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels) |
90 | { | 96 | { |
91 | unsigned long flags_old = | 97 | unsigned long flags_old = |
92 | __sync_fetch_and_or(&line6pcm->flags, channels); | 98 | __sync_fetch_and_or(&line6pcm->flags, channels); |
93 | unsigned long flags_new = flags_old | channels; | 99 | unsigned long flags_new = flags_old | channels; |
94 | int err = 0; | 100 | int err = 0; |
95 | 101 | ||
96 | line6pcm->prev_fbuf = NULL; | 102 | line6pcm->prev_fbuf = NULL; |
97 | 103 | ||
98 | if (((flags_old & MASK_CAPTURE) == 0) && | 104 | if (test_flags(flags_old, flags_new, MASK_CAPTURE)) { |
99 | ((flags_new & MASK_CAPTURE) != 0)) { | ||
100 | /* | 105 | /* |
101 | Waiting for completion of active URBs in the stop handler is | 106 | Waiting for completion of active URBs in the stop handler is |
102 | a bug, we therefore report an error if capturing is restarted | 107 | 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) | |||
105 | if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) | 110 | if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) |
106 | return -EBUSY; | 111 | return -EBUSY; |
107 | 112 | ||
113 | if (!(flags_new & MASK_PCM_ALSA_CAPTURE)) { | ||
114 | err = line6_alloc_capture_buffer(line6pcm); | ||
115 | |||
116 | if (err < 0) | ||
117 | goto pcm_start_error; | ||
118 | } | ||
119 | |||
108 | line6pcm->count_in = 0; | 120 | line6pcm->count_in = 0; |
109 | line6pcm->prev_fsize = 0; | 121 | line6pcm->prev_fsize = 0; |
110 | err = line6_submit_audio_in_all_urbs(line6pcm); | 122 | err = line6_submit_audio_in_all_urbs(line6pcm); |
111 | 123 | ||
112 | if (err < 0) { | 124 | if (err < 0) |
113 | __sync_fetch_and_and(&line6pcm->flags, ~channels); | 125 | goto pcm_start_error; |
114 | return err; | ||
115 | } | ||
116 | } | 126 | } |
117 | 127 | ||
118 | if (((flags_old & MASK_PLAYBACK) == 0) && | 128 | if (test_flags(flags_old, flags_new, MASK_PLAYBACK)) { |
119 | ((flags_new & MASK_PLAYBACK) != 0)) { | ||
120 | /* | 129 | /* |
121 | See comment above regarding PCM restart. | 130 | See comment above regarding PCM restart. |
122 | */ | 131 | */ |
123 | if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) | 132 | if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) |
124 | return -EBUSY; | 133 | return -EBUSY; |
125 | 134 | ||
135 | if (!(flags_new & MASK_PCM_ALSA_PLAYBACK)) { | ||
136 | err = line6_alloc_playback_buffer(line6pcm); | ||
137 | |||
138 | if (err < 0) | ||
139 | goto pcm_start_error; | ||
140 | } | ||
141 | |||
126 | line6pcm->count_out = 0; | 142 | line6pcm->count_out = 0; |
127 | err = line6_submit_audio_out_all_urbs(line6pcm); | 143 | err = line6_submit_audio_out_all_urbs(line6pcm); |
128 | 144 | ||
129 | if (err < 0) { | 145 | if (err < 0) |
130 | __sync_fetch_and_and(&line6pcm->flags, ~channels); | 146 | goto pcm_start_error; |
131 | return err; | ||
132 | } | ||
133 | } | 147 | } |
134 | 148 | ||
135 | return 0; | 149 | return 0; |
150 | |||
151 | pcm_start_error: | ||
152 | __sync_fetch_and_and(&line6pcm->flags, ~channels); | ||
153 | return err; | ||
136 | } | 154 | } |
137 | 155 | ||
138 | int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels) | 156 | 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) | |||
141 | __sync_fetch_and_and(&line6pcm->flags, ~channels); | 159 | __sync_fetch_and_and(&line6pcm->flags, ~channels); |
142 | unsigned long flags_new = flags_old & ~channels; | 160 | unsigned long flags_new = flags_old & ~channels; |
143 | 161 | ||
144 | if (((flags_old & MASK_CAPTURE) != 0) && | 162 | if (test_flags(flags_new, flags_old, MASK_CAPTURE)) { |
145 | ((flags_new & MASK_CAPTURE) == 0)) { | ||
146 | line6_unlink_audio_in_urbs(line6pcm); | 163 | line6_unlink_audio_in_urbs(line6pcm); |
164 | |||
165 | if (!(flags_old & MASK_PCM_ALSA_CAPTURE)) | ||
166 | line6_free_capture_buffer(line6pcm); | ||
147 | } | 167 | } |
148 | 168 | ||
149 | if (((flags_old & MASK_PLAYBACK) != 0) && | 169 | if (test_flags(flags_new, flags_old, MASK_PLAYBACK)) { |
150 | ((flags_new & MASK_PLAYBACK) == 0)) { | ||
151 | line6_unlink_audio_out_urbs(line6pcm); | 170 | line6_unlink_audio_out_urbs(line6pcm); |
171 | |||
172 | if (!(flags_old & MASK_PCM_ALSA_PLAYBACK)) | ||
173 | line6_free_playback_buffer(line6pcm); | ||
152 | } | 174 | } |
153 | 175 | ||
154 | return 0; | 176 | return 0; |
@@ -476,18 +498,21 @@ int snd_line6_prepare(struct snd_pcm_substream *substream) | |||
476 | 498 | ||
477 | switch (substream->stream) { | 499 | switch (substream->stream) { |
478 | case SNDRV_PCM_STREAM_PLAYBACK: | 500 | case SNDRV_PCM_STREAM_PLAYBACK: |
479 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); | 501 | if ((line6pcm->flags & MASK_PLAYBACK) == 0) |
502 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); | ||
503 | |||
480 | break; | 504 | break; |
481 | 505 | ||
482 | case SNDRV_PCM_STREAM_CAPTURE: | 506 | case SNDRV_PCM_STREAM_CAPTURE: |
483 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); | 507 | if ((line6pcm->flags & MASK_CAPTURE) == 0) |
508 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); | ||
509 | |||
484 | break; | 510 | break; |
485 | 511 | ||
486 | default: | 512 | default: |
487 | MISSING_CASE; | 513 | MISSING_CASE; |
488 | } | 514 | } |
489 | 515 | ||
490 | |||
491 | if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) { | 516 | if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) { |
492 | line6pcm->count_out = 0; | 517 | line6pcm->count_out = 0; |
493 | line6pcm->pos_out = 0; | 518 | line6pcm->pos_out = 0; |
diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c index 9a51b92c0948..4152db2328b7 100644 --- a/drivers/staging/line6/playback.c +++ b/drivers/staging/line6/playback.c | |||
@@ -351,6 +351,31 @@ void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) | |||
351 | wait_clear_audio_out_urbs(line6pcm); | 351 | wait_clear_audio_out_urbs(line6pcm); |
352 | } | 352 | } |
353 | 353 | ||
354 | int line6_alloc_playback_buffer(struct snd_line6_pcm *line6pcm) | ||
355 | { | ||
356 | /* We may be invoked multiple times in a row so allocate once only */ | ||
357 | if (line6pcm->buffer_out) | ||
358 | return 0; | ||
359 | |||
360 | line6pcm->buffer_out = | ||
361 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | ||
362 | line6pcm->max_packet_size, GFP_KERNEL); | ||
363 | |||
364 | if (!line6pcm->buffer_out) { | ||
365 | dev_err(line6pcm->line6->ifcdev, | ||
366 | "cannot malloc playback buffer\n"); | ||
367 | return -ENOMEM; | ||
368 | } | ||
369 | |||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm) | ||
374 | { | ||
375 | kfree(line6pcm->buffer_out); | ||
376 | line6pcm->buffer_out = NULL; | ||
377 | } | ||
378 | |||
354 | /* | 379 | /* |
355 | Callback for completed playback URB. | 380 | Callback for completed playback URB. |
356 | */ | 381 | */ |
@@ -459,16 +484,11 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, | |||
459 | } | 484 | } |
460 | /* -- [FD] end */ | 485 | /* -- [FD] end */ |
461 | 486 | ||
462 | /* We may be invoked multiple times in a row so allocate once only */ | 487 | if ((line6pcm->flags & MASK_PLAYBACK) == 0) { |
463 | if (!line6pcm->buffer_out) | 488 | ret = line6_alloc_playback_buffer(line6pcm); |
464 | line6pcm->buffer_out = | ||
465 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | ||
466 | line6pcm->max_packet_size, GFP_KERNEL); | ||
467 | 489 | ||
468 | if (!line6pcm->buffer_out) { | 490 | if (ret < 0) |
469 | dev_err(line6pcm->line6->ifcdev, | 491 | return ret; |
470 | "cannot malloc playback buffer\n"); | ||
471 | return -ENOMEM; | ||
472 | } | 492 | } |
473 | 493 | ||
474 | ret = snd_pcm_lib_malloc_pages(substream, | 494 | ret = snd_pcm_lib_malloc_pages(substream, |
@@ -485,9 +505,11 @@ static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) | |||
485 | { | 505 | { |
486 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | 506 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); |
487 | 507 | ||
488 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); | 508 | if ((line6pcm->flags & MASK_PLAYBACK) == 0) { |
489 | kfree(line6pcm->buffer_out); | 509 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); |
490 | line6pcm->buffer_out = NULL; | 510 | line6_free_playback_buffer(line6pcm); |
511 | } | ||
512 | |||
491 | return snd_pcm_lib_free_pages(substream); | 513 | return snd_pcm_lib_free_pages(substream); |
492 | } | 514 | } |
493 | 515 | ||
diff --git a/drivers/staging/line6/playback.h b/drivers/staging/line6/playback.h index f2fc8c0526e3..02487ff24538 100644 --- a/drivers/staging/line6/playback.h +++ b/drivers/staging/line6/playback.h | |||
@@ -29,7 +29,9 @@ | |||
29 | 29 | ||
30 | extern struct snd_pcm_ops snd_line6_playback_ops; | 30 | extern struct snd_pcm_ops snd_line6_playback_ops; |
31 | 31 | ||
32 | extern int line6_alloc_playback_buffer(struct snd_line6_pcm *line6pcm); | ||
32 | extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); | 33 | extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); |
34 | extern void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm); | ||
33 | extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); | 35 | extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); |
34 | extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm); | 36 | extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm); |
35 | extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm | 37 | extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm |
diff --git a/drivers/staging/line6/revision.h b/drivers/staging/line6/revision.h index 350d0dfff8f8..b4eee2b73831 100644 --- a/drivers/staging/line6/revision.h +++ b/drivers/staging/line6/revision.h | |||
@@ -1,4 +1,4 @@ | |||
1 | #ifndef DRIVER_REVISION | 1 | #ifndef DRIVER_REVISION |
2 | /* current subversion revision */ | 2 | /* current subversion revision */ |
3 | #define DRIVER_REVISION " (revision 690)" | 3 | #define DRIVER_REVISION " (904)" |
4 | #endif | 4 | #endif |