Commit 6b02a17ee5cd5d200dbe4a285a4e750f70884967

Authored by Markus Grabner
Committed by Greg Kroah-Hartman
1 parent 665f3f506b

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>

Showing 6 changed files with 118 additions and 45 deletions Side-by-side Diff

drivers/staging/line6/capture.c
... ... @@ -193,6 +193,31 @@
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 222 * Callback for completed capture URB.
198 223 */
199 224  
... ... @@ -316,16 +341,11 @@
316 341 }
317 342 /* -- [FD] end */
318 343  
319   - /* We may be invoked multiple times in a row so allocate once only */
320   - if (!line6pcm->buffer_in)
321   - line6pcm->buffer_in =
322   - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
323   - line6pcm->max_packet_size, GFP_KERNEL);
  344 + if ((line6pcm->flags & MASK_CAPTURE) == 0) {
  345 + ret = line6_alloc_capture_buffer(line6pcm);
324 346  
325   - if (!line6pcm->buffer_in) {
326   - dev_err(line6pcm->line6->ifcdev,
327   - "cannot malloc capture buffer\n");
328   - return -ENOMEM;
  347 + if (ret < 0)
  348 + return ret;
329 349 }
330 350  
331 351 ret = snd_pcm_lib_malloc_pages(substream,
... ... @@ -342,9 +362,11 @@
342 362 {
343 363 struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
344 364  
345   - line6_unlink_wait_clear_audio_in_urbs(line6pcm);
346   - kfree(line6pcm->buffer_in);
347   - line6pcm->buffer_in = NULL;
  365 + if ((line6pcm->flags & MASK_CAPTURE) == 0) {
  366 + line6_unlink_wait_clear_audio_in_urbs(line6pcm);
  367 + line6_free_capture_buffer(line6pcm);
  368 + }
  369 +
348 370 return snd_pcm_lib_free_pages(substream);
349 371 }
350 372  
drivers/staging/line6/capture.h
... ... @@ -19,11 +19,13 @@
19 19  
20 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 23 extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf,
23 24 int fsize);
24 25 extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm,
25 26 int length);
26 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 29 extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm);
28 30 extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm);
29 31 extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm
drivers/staging/line6/pcm.c
... ... @@ -86,17 +86,22 @@
86 86  
87 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 95 int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels)
90 96 {
91 97 unsigned long flags_old =
92 98 __sync_fetch_and_or(&line6pcm->flags, channels);
93 99 unsigned long flags_new = flags_old | channels;
94 100 int err = 0;
95   -
  101 +
96 102 line6pcm->prev_fbuf = NULL;
97 103  
98   - if (((flags_old & MASK_CAPTURE) == 0) &&
99   - ((flags_new & MASK_CAPTURE) != 0)) {
  104 + if (test_flags(flags_old, flags_new, MASK_CAPTURE)) {
100 105 /*
101 106 Waiting for completion of active URBs in the stop handler is
102 107 a bug, we therefore report an error if capturing is restarted
103 108  
104 109  
105 110  
106 111  
107 112  
... ... @@ -105,34 +110,47 @@
105 110 if (line6pcm->active_urb_in | line6pcm->unlink_urb_in)
106 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 120 line6pcm->count_in = 0;
109 121 line6pcm->prev_fsize = 0;
110 122 err = line6_submit_audio_in_all_urbs(line6pcm);
111 123  
112   - if (err < 0) {
113   - __sync_fetch_and_and(&line6pcm->flags, ~channels);
114   - return err;
115   - }
  124 + if (err < 0)
  125 + goto pcm_start_error;
116 126 }
117 127  
118   - if (((flags_old & MASK_PLAYBACK) == 0) &&
119   - ((flags_new & MASK_PLAYBACK) != 0)) {
  128 + if (test_flags(flags_old, flags_new, MASK_PLAYBACK)) {
120 129 /*
121 130 See comment above regarding PCM restart.
122 131 */
123 132 if (line6pcm->active_urb_out | line6pcm->unlink_urb_out)
124 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 142 line6pcm->count_out = 0;
127 143 err = line6_submit_audio_out_all_urbs(line6pcm);
128 144  
129   - if (err < 0) {
130   - __sync_fetch_and_and(&line6pcm->flags, ~channels);
131   - return err;
132   - }
  145 + if (err < 0)
  146 + goto pcm_start_error;
133 147 }
134 148  
135 149 return 0;
  150 +
  151 +pcm_start_error:
  152 + __sync_fetch_and_and(&line6pcm->flags, ~channels);
  153 + return err;
136 154 }
137 155  
138 156 int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels)
139 157  
140 158  
141 159  
... ... @@ -141,14 +159,18 @@
141 159 __sync_fetch_and_and(&line6pcm->flags, ~channels);
142 160 unsigned long flags_new = flags_old & ~channels;
143 161  
144   - if (((flags_old & MASK_CAPTURE) != 0) &&
145   - ((flags_new & MASK_CAPTURE) == 0)) {
  162 + if (test_flags(flags_new, flags_old, MASK_CAPTURE)) {
146 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) &&
150   - ((flags_new & MASK_PLAYBACK) == 0)) {
  169 + if (test_flags(flags_new, flags_old, MASK_PLAYBACK)) {
151 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 176 return 0;
155 177  
156 178  
... ... @@ -476,17 +498,20 @@
476 498  
477 499 switch (substream->stream) {
478 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 504 break;
481 505  
482 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 510 break;
485 511  
486 512 default:
487 513 MISSING_CASE;
488 514 }
489   -
490 515  
491 516 if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) {
492 517 line6pcm->count_out = 0;
drivers/staging/line6/playback.c
... ... @@ -351,6 +351,31 @@
351 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 380 Callback for completed playback URB.
356 381 */
357 382  
... ... @@ -459,16 +484,11 @@
459 484 }
460 485 /* -- [FD] end */
461 486  
462   - /* We may be invoked multiple times in a row so allocate once only */
463   - if (!line6pcm->buffer_out)
464   - line6pcm->buffer_out =
465   - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
466   - line6pcm->max_packet_size, GFP_KERNEL);
  487 + if ((line6pcm->flags & MASK_PLAYBACK) == 0) {
  488 + ret = line6_alloc_playback_buffer(line6pcm);
467 489  
468   - if (!line6pcm->buffer_out) {
469   - dev_err(line6pcm->line6->ifcdev,
470   - "cannot malloc playback buffer\n");
471   - return -ENOMEM;
  490 + if (ret < 0)
  491 + return ret;
472 492 }
473 493  
474 494 ret = snd_pcm_lib_malloc_pages(substream,
... ... @@ -485,9 +505,11 @@
485 505 {
486 506 struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
487 507  
488   - line6_unlink_wait_clear_audio_out_urbs(line6pcm);
489   - kfree(line6pcm->buffer_out);
490   - line6pcm->buffer_out = NULL;
  508 + if ((line6pcm->flags & MASK_PLAYBACK) == 0) {
  509 + line6_unlink_wait_clear_audio_out_urbs(line6pcm);
  510 + line6_free_playback_buffer(line6pcm);
  511 + }
  512 +
491 513 return snd_pcm_lib_free_pages(substream);
492 514 }
493 515  
drivers/staging/line6/playback.h
... ... @@ -29,7 +29,9 @@
29 29  
30 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 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 35 extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm);
34 36 extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm);
35 37 extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm
drivers/staging/line6/revision.h
1 1 #ifndef DRIVER_REVISION
2 2 /* current subversion revision */
3   -#define DRIVER_REVISION " (revision 690)"
  3 +#define DRIVER_REVISION " (904)"
4 4 #endif