Skip to content

Commit

Permalink
ALSA: snd_usb_caiaq: track submitted output urbs
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reported-and-tested-by: Matej Laitl <[email protected]>
Cc: Sarah Sharp <[email protected]>
Cc: [email protected]
Signed-off-by: Takashi Iwai <[email protected]>
  • Loading branch information
zonque authored and tiwai committed Aug 14, 2011
1 parent c012cdc commit da6094e
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 4 deletions.
31 changes: 27 additions & 4 deletions sound/usb/caiaq/audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,12 @@ static void stream_stop(struct snd_usb_caiaqdev *dev)

for (i = 0; i < N_URBS; i++) {
usb_kill_urb(dev->data_urbs_in[i]);
usb_kill_urb(dev->data_urbs_out[i]);

if (test_bit(i, &dev->outurb_active_mask))
usb_kill_urb(dev->data_urbs_out[i]);
}

dev->outurb_active_mask = 0;
}

static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream)
Expand Down Expand Up @@ -612,8 +616,8 @@ static void read_completed(struct urb *urb)
{
struct snd_usb_caiaq_cb_info *info = urb->context;
struct snd_usb_caiaqdev *dev;
struct urb *out;
int frame, len, send_it = 0, outframe = 0;
struct urb *out = NULL;
int i, frame, len, send_it = 0, outframe = 0;
size_t offset = 0;

if (urb->status || !info)
Expand All @@ -624,7 +628,17 @@ static void read_completed(struct urb *urb)
if (!dev->streaming)
return;

out = dev->data_urbs_out[info->index];
/* find an unused output urb that is unused */
for (i = 0; i < N_URBS; i++)
if (test_and_set_bit(i, &dev->outurb_active_mask) == 0) {
out = dev->data_urbs_out[i];
break;
}

if (!out) {
log("Unable to find an output urb to use\n");
goto requeue;
}

/* read the recently received packet and send back one which has
* the same layout */
Expand Down Expand Up @@ -655,8 +669,12 @@ static void read_completed(struct urb *urb)
out->number_of_packets = outframe;
out->transfer_flags = URB_ISO_ASAP;
usb_submit_urb(out, GFP_ATOMIC);
} else {
struct snd_usb_caiaq_cb_info *oinfo = out->context;
clear_bit(oinfo->index, &dev->outurb_active_mask);
}

requeue:
/* re-submit inbound urb */
for (frame = 0; frame < FRAMES_PER_URB; frame++) {
urb->iso_frame_desc[frame].offset = BYTES_PER_FRAME * frame;
Expand All @@ -678,6 +696,8 @@ static void write_completed(struct urb *urb)
dev->output_running = 1;
wake_up(&dev->prepare_wait_queue);
}

clear_bit(info->index, &dev->outurb_active_mask);
}

static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
Expand Down Expand Up @@ -829,6 +849,9 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
if (!dev->data_cb_info)
return -ENOMEM;

dev->outurb_active_mask = 0;
BUILD_BUG_ON(N_URBS > (sizeof(dev->outurb_active_mask) * 8));

for (i = 0; i < N_URBS; i++) {
dev->data_cb_info[i].dev = dev;
dev->data_cb_info[i].index = i;
Expand Down
1 change: 1 addition & 0 deletions sound/usb/caiaq/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ struct snd_usb_caiaqdev {
int input_panic, output_panic, warned;
char *audio_in_buf, *audio_out_buf;
unsigned int samplerates, bpp;
unsigned long outurb_active_mask;

struct snd_pcm_substream *sub_playback[MAX_STREAMS];
struct snd_pcm_substream *sub_capture[MAX_STREAMS];
Expand Down

0 comments on commit da6094e

Please sign in to comment.