aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb/caiaq
diff options
context:
space:
mode:
authorDaniel Mack <zonque@gmail.com>2011-08-14 05:31:16 -0400
committerTakashi Iwai <tiwai@suse.de>2011-08-14 12:10:43 -0400
commitda6094ea7d3c2295473d8f5134279307255d6ebf (patch)
treee27db43aec5a48586535a82383be3107050bdac0 /sound/usb/caiaq
parentc012cdc858db36ff2314469930bfcfb00eef6f29 (diff)
ALSA: snd_usb_caiaq: track submitted output urbs
The snd_usb_caiaq driver currently assumes that output urbs are serviced in time and doesn't track when and whether they are given back by the USB core. That usually works fine, but due to temporary limitations of the XHCI stack, we faced that urbs were submitted more than once with this approach. As it's no good practice to fire and forget urbs anyway, this patch introduces a proper bit mask to track which requests have been submitted and given back. That alone however doesn't make the driver work in case the host controller is broken and doesn't give back urbs at all, and the output stream will stop once all pre-allocated output urbs are consumed. But it does prevent crashes of the controller stack in such cases. See http://bugzilla.kernel.org/show_bug.cgi?id=40702 for more details. Signed-off-by: Daniel Mack <zonque@gmail.com> Reported-and-tested-by: Matej Laitl <matej@laitl.cz> Cc: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: stable@kernel.org Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/caiaq')
-rw-r--r--sound/usb/caiaq/audio.c31
-rw-r--r--sound/usb/caiaq/device.h1
2 files changed, 28 insertions, 4 deletions
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index aa52b3e13bb5..2cf87f5afed4 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -139,8 +139,12 @@ static void stream_stop(struct snd_usb_caiaqdev *dev)
139 139
140 for (i = 0; i < N_URBS; i++) { 140 for (i = 0; i < N_URBS; i++) {
141 usb_kill_urb(dev->data_urbs_in[i]); 141 usb_kill_urb(dev->data_urbs_in[i]);
142 usb_kill_urb(dev->data_urbs_out[i]); 142
143 if (test_bit(i, &dev->outurb_active_mask))
144 usb_kill_urb(dev->data_urbs_out[i]);
143 } 145 }
146
147 dev->outurb_active_mask = 0;
144} 148}
145 149
146static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream) 150static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream)
@@ -612,8 +616,8 @@ static void read_completed(struct urb *urb)
612{ 616{
613 struct snd_usb_caiaq_cb_info *info = urb->context; 617 struct snd_usb_caiaq_cb_info *info = urb->context;
614 struct snd_usb_caiaqdev *dev; 618 struct snd_usb_caiaqdev *dev;
615 struct urb *out; 619 struct urb *out = NULL;
616 int frame, len, send_it = 0, outframe = 0; 620 int i, frame, len, send_it = 0, outframe = 0;
617 size_t offset = 0; 621 size_t offset = 0;
618 622
619 if (urb->status || !info) 623 if (urb->status || !info)
@@ -624,7 +628,17 @@ static void read_completed(struct urb *urb)
624 if (!dev->streaming) 628 if (!dev->streaming)
625 return; 629 return;
626 630
627 out = dev->data_urbs_out[info->index]; 631 /* find an unused output urb that is unused */
632 for (i = 0; i < N_URBS; i++)
633 if (test_and_set_bit(i, &dev->outurb_active_mask) == 0) {
634 out = dev->data_urbs_out[i];
635 break;
636 }
637
638 if (!out) {
639 log("Unable to find an output urb to use\n");
640 goto requeue;
641 }
628 642
629 /* read the recently received packet and send back one which has 643 /* read the recently received packet and send back one which has
630 * the same layout */ 644 * the same layout */
@@ -655,8 +669,12 @@ static void read_completed(struct urb *urb)
655 out->number_of_packets = outframe; 669 out->number_of_packets = outframe;
656 out->transfer_flags = URB_ISO_ASAP; 670 out->transfer_flags = URB_ISO_ASAP;
657 usb_submit_urb(out, GFP_ATOMIC); 671 usb_submit_urb(out, GFP_ATOMIC);
672 } else {
673 struct snd_usb_caiaq_cb_info *oinfo = out->context;
674 clear_bit(oinfo->index, &dev->outurb_active_mask);
658 } 675 }
659 676
677requeue:
660 /* re-submit inbound urb */ 678 /* re-submit inbound urb */
661 for (frame = 0; frame < FRAMES_PER_URB; frame++) { 679 for (frame = 0; frame < FRAMES_PER_URB; frame++) {
662 urb->iso_frame_desc[frame].offset = BYTES_PER_FRAME * frame; 680 urb->iso_frame_desc[frame].offset = BYTES_PER_FRAME * frame;
@@ -678,6 +696,8 @@ static void write_completed(struct urb *urb)
678 dev->output_running = 1; 696 dev->output_running = 1;
679 wake_up(&dev->prepare_wait_queue); 697 wake_up(&dev->prepare_wait_queue);
680 } 698 }
699
700 clear_bit(info->index, &dev->outurb_active_mask);
681} 701}
682 702
683static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret) 703static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
@@ -829,6 +849,9 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
829 if (!dev->data_cb_info) 849 if (!dev->data_cb_info)
830 return -ENOMEM; 850 return -ENOMEM;
831 851
852 dev->outurb_active_mask = 0;
853 BUILD_BUG_ON(N_URBS > (sizeof(dev->outurb_active_mask) * 8));
854
832 for (i = 0; i < N_URBS; i++) { 855 for (i = 0; i < N_URBS; i++) {
833 dev->data_cb_info[i].dev = dev; 856 dev->data_cb_info[i].dev = dev;
834 dev->data_cb_info[i].index = i; 857 dev->data_cb_info[i].index = i;
diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h
index b2b310194ffa..3f9c6339ae90 100644
--- a/sound/usb/caiaq/device.h
+++ b/sound/usb/caiaq/device.h
@@ -96,6 +96,7 @@ struct snd_usb_caiaqdev {
96 int input_panic, output_panic, warned; 96 int input_panic, output_panic, warned;
97 char *audio_in_buf, *audio_out_buf; 97 char *audio_in_buf, *audio_out_buf;
98 unsigned int samplerates, bpp; 98 unsigned int samplerates, bpp;
99 unsigned long outurb_active_mask;
99 100
100 struct snd_pcm_substream *sub_playback[MAX_STREAMS]; 101 struct snd_pcm_substream *sub_playback[MAX_STREAMS];
101 struct snd_pcm_substream *sub_capture[MAX_STREAMS]; 102 struct snd_pcm_substream *sub_capture[MAX_STREAMS];