Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Markus Grabner and committed by
Greg Kroah-Hartman
6b02a17e 665f3f50

+118 -45
+34 -12
drivers/staging/line6/capture.c
··· 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 */ ··· 341 316 } 342 317 /* -- [FD] end */ 343 318 344 - /* We may be invoked multiple times in a row so allocate once only */ 345 - if (!line6pcm->buffer_in) 346 - line6pcm->buffer_in = 347 - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * 348 - line6pcm->max_packet_size, GFP_KERNEL); 319 + if ((line6pcm->flags & MASK_CAPTURE) == 0) { 320 + ret = line6_alloc_capture_buffer(line6pcm); 349 321 350 - if (!line6pcm->buffer_in) { 351 - dev_err(line6pcm->line6->ifcdev, 352 - "cannot malloc capture buffer\n"); 353 - return -ENOMEM; 322 + if (ret < 0) 323 + return ret; 354 324 } 355 325 356 326 ret = snd_pcm_lib_malloc_pages(substream, ··· 362 342 { 363 343 struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); 364 344 365 - line6_unlink_wait_clear_audio_in_urbs(line6pcm); 366 - kfree(line6pcm->buffer_in); 367 - line6pcm->buffer_in = NULL; 345 + if ((line6pcm->flags & MASK_CAPTURE) == 0) { 346 + line6_unlink_wait_clear_audio_in_urbs(line6pcm); 347 + line6_free_capture_buffer(line6pcm); 348 + } 349 + 368 350 return snd_pcm_lib_free_pages(substream); 369 351 } 370 352
+2
drivers/staging/line6/capture.h
··· 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
+45 -20
drivers/staging/line6/pcm.c
··· 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 ··· 110 105 if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) 111 106 return -EBUSY; 112 107 108 + if (!(flags_new & MASK_PCM_ALSA_CAPTURE)) { 109 + err = line6_alloc_capture_buffer(line6pcm); 110 + 111 + if (err < 0) 112 + goto pcm_start_error; 113 + } 114 + 113 115 line6pcm->count_in = 0; 114 116 line6pcm->prev_fsize = 0; 115 117 err = line6_submit_audio_in_all_urbs(line6pcm); 116 118 117 - if (err < 0) { 118 - __sync_fetch_and_and(&line6pcm->flags, ~channels); 119 - return err; 120 - } 119 + if (err < 0) 120 + goto pcm_start_error; 121 121 } 122 122 123 - if (((flags_old & MASK_PLAYBACK) == 0) && 124 - ((flags_new & MASK_PLAYBACK) != 0)) { 123 + if (test_flags(flags_old, flags_new, MASK_PLAYBACK)) { 125 124 /* 126 125 See comment above regarding PCM restart. 127 126 */ 128 127 if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) 129 128 return -EBUSY; 130 129 130 + if (!(flags_new & MASK_PCM_ALSA_PLAYBACK)) { 131 + err = line6_alloc_playback_buffer(line6pcm); 132 + 133 + if (err < 0) 134 + goto pcm_start_error; 135 + } 136 + 131 137 line6pcm->count_out = 0; 132 138 err = line6_submit_audio_out_all_urbs(line6pcm); 133 139 134 - if (err < 0) { 135 - __sync_fetch_and_and(&line6pcm->flags, ~channels); 136 - return err; 137 - } 140 + if (err < 0) 141 + goto pcm_start_error; 138 142 } 139 143 140 144 return 0; 145 + 146 + pcm_start_error: 147 + __sync_fetch_and_and(&line6pcm->flags, ~channels); 148 + return err; 141 149 } 142 150 143 151 int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels) ··· 159 141 __sync_fetch_and_and(&line6pcm->flags, ~channels); 160 142 unsigned long flags_new = flags_old & ~channels; 161 143 162 - if (((flags_old & MASK_CAPTURE) != 0) && 163 - ((flags_new & MASK_CAPTURE) == 0)) { 144 + if (test_flags(flags_new, flags_old, MASK_CAPTURE)) { 164 145 line6_unlink_audio_in_urbs(line6pcm); 146 + 147 + if (!(flags_old & MASK_PCM_ALSA_CAPTURE)) 148 + line6_free_capture_buffer(line6pcm); 165 149 } 166 150 167 - if (((flags_old & MASK_PLAYBACK) != 0) && 168 - ((flags_new & MASK_PLAYBACK) == 0)) { 151 + if (test_flags(flags_new, flags_old, MASK_PLAYBACK)) { 169 152 line6_unlink_audio_out_urbs(line6pcm); 153 + 154 + if (!(flags_old & MASK_PCM_ALSA_PLAYBACK)) 155 + line6_free_playback_buffer(line6pcm); 170 156 } 171 157 172 158 return 0; ··· 498 476 499 477 switch (substream->stream) { 500 478 case SNDRV_PCM_STREAM_PLAYBACK: 501 - line6_unlink_wait_clear_audio_out_urbs(line6pcm); 479 + if ((line6pcm->flags & MASK_PLAYBACK) == 0) 480 + line6_unlink_wait_clear_audio_out_urbs(line6pcm); 481 + 502 482 break; 503 483 504 484 case SNDRV_PCM_STREAM_CAPTURE: 505 - line6_unlink_wait_clear_audio_in_urbs(line6pcm); 485 + if ((line6pcm->flags & MASK_CAPTURE) == 0) 486 + line6_unlink_wait_clear_audio_in_urbs(line6pcm); 487 + 506 488 break; 507 489 508 490 default: 509 491 MISSING_CASE; 510 492 } 511 - 512 493 513 494 if (!test_and_set_bit(BIT_PREPARED, &line6pcm->flags)) { 514 495 line6pcm->count_out = 0;
+34 -12
drivers/staging/line6/playback.c
··· 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 */ ··· 484 459 } 485 460 /* -- [FD] end */ 486 461 487 - /* We may be invoked multiple times in a row so allocate once only */ 488 - if (!line6pcm->buffer_out) 489 - line6pcm->buffer_out = 490 - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * 491 - line6pcm->max_packet_size, GFP_KERNEL); 462 + if ((line6pcm->flags & MASK_PLAYBACK) == 0) { 463 + ret = line6_alloc_playback_buffer(line6pcm); 492 464 493 - if (!line6pcm->buffer_out) { 494 - dev_err(line6pcm->line6->ifcdev, 495 - "cannot malloc playback buffer\n"); 496 - return -ENOMEM; 465 + if (ret < 0) 466 + return ret; 497 467 } 498 468 499 469 ret = snd_pcm_lib_malloc_pages(substream, ··· 505 485 { 506 486 struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); 507 487 508 - line6_unlink_wait_clear_audio_out_urbs(line6pcm); 509 - kfree(line6pcm->buffer_out); 510 - line6pcm->buffer_out = NULL; 488 + if ((line6pcm->flags & MASK_PLAYBACK) == 0) { 489 + line6_unlink_wait_clear_audio_out_urbs(line6pcm); 490 + line6_free_playback_buffer(line6pcm); 491 + } 492 + 511 493 return snd_pcm_lib_free_pages(substream); 512 494 } 513 495
+2
drivers/staging/line6/playback.h
··· 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
+1 -1
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