Skip to content

Commit

Permalink
[ALSA] usb-audio: throttle MIDI URB resubmits on USB errors
Browse files Browse the repository at this point in the history
USB generic driver
When a USB error occurs that might indicate that the device has been
unplugged, don't resubmit the URB immediately to prevent flooding the
log with error messages before khubd has us disconnect()ed.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
  • Loading branch information
Clemens Ladisch authored and Jaroslav Kysela committed Aug 30, 2005
1 parent d568121 commit c884697
Showing 1 changed file with 64 additions and 13 deletions.
77 changes: 64 additions & 13 deletions sound/usb/usbmidi.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <linux/string.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/usb.h>
#include <sound/core.h>
#include <sound/minors.h>
Expand All @@ -56,6 +57,12 @@
*/
/* #define DUMP_PACKETS */

/*
* how long to wait after some USB errors, so that khubd can disconnect() us
* without too many spurious errors
*/
#define ERROR_DELAY_JIFFIES (HZ / 10)


MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("USB Audio/MIDI helper module");
Expand Down Expand Up @@ -100,6 +107,7 @@ struct snd_usb_midi {
snd_rawmidi_t* rmidi;
struct usb_protocol_ops* usb_protocol_ops;
struct list_head list;
struct timer_list error_timer;

struct snd_usb_midi_endpoint {
snd_usb_midi_out_endpoint_t *out;
Expand Down Expand Up @@ -141,7 +149,8 @@ struct snd_usb_midi_in_endpoint {
struct usbmidi_in_port {
snd_rawmidi_substream_t* substream;
} ports[0x10];
int seen_f5;
u8 seen_f5;
u8 error_resubmit;
int current_port;
};

Expand All @@ -167,14 +176,22 @@ static int snd_usbmidi_submit_urb(struct urb* urb, int flags)
*/
static int snd_usbmidi_urb_error(int status)
{
if (status == -ENOENT)
return status; /* killed */
if (status == -EILSEQ ||
status == -ECONNRESET ||
status == -ETIMEDOUT)
return -ENODEV; /* device removed/shutdown */
snd_printk(KERN_ERR "urb status %d\n", status);
return 0; /* continue */
switch (status) {
/* manually unlinked, or device gone */
case -ENOENT:
case -ECONNRESET:
case -ESHUTDOWN:
case -ENODEV:
return -ENODEV;
/* errors that might occur during unplugging */
case -EPROTO: /* EHCI */
case -ETIMEDOUT: /* OHCI */
case -EILSEQ: /* UHCI */
return -EIO;
default:
snd_printk(KERN_ERR "urb status %d\n", status);
return 0; /* continue */
}
}

/*
Expand Down Expand Up @@ -218,8 +235,15 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb, struct pt_regs *regs)
ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer,
urb->actual_length);
} else {
if (snd_usbmidi_urb_error(urb->status) < 0)
int err = snd_usbmidi_urb_error(urb->status);
if (err < 0) {
if (err != -ENODEV) {
ep->error_resubmit = 1;
mod_timer(&ep->umidi->error_timer,
jiffies + ERROR_DELAY_JIFFIES);
}
return;
}
}

if (usb_pipe_needs_resubmit(urb->pipe)) {
Expand All @@ -236,8 +260,13 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb, struct pt_regs *regs)
ep->urb_active = 0;
spin_unlock(&ep->buffer_lock);
if (urb->status < 0) {
if (snd_usbmidi_urb_error(urb->status) < 0)
int err = snd_usbmidi_urb_error(urb->status);
if (err < 0) {
if (err != -ENODEV)
mod_timer(&ep->umidi->error_timer,
jiffies + ERROR_DELAY_JIFFIES);
return;
}
}
snd_usbmidi_do_output(ep);
}
Expand Down Expand Up @@ -276,6 +305,24 @@ static void snd_usbmidi_out_tasklet(unsigned long data)
snd_usbmidi_do_output(ep);
}

/* called after transfers had been interrupted due to some USB error */
static void snd_usbmidi_error_timer(unsigned long data)
{
snd_usb_midi_t *umidi = (snd_usb_midi_t *)data;
int i;

for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
snd_usb_midi_in_endpoint_t *in = umidi->endpoints[i].in;
if (in && in->error_resubmit) {
in->error_resubmit = 0;
in->urb->dev = umidi->chip->dev;
snd_usbmidi_submit_urb(in->urb, GFP_ATOMIC);
}
if (umidi->endpoints[i].out)
snd_usbmidi_do_output(umidi->endpoints[i].out);
}
}

/* helper function to send static data that may not DMA-able */
static int send_bulk_static_data(snd_usb_midi_out_endpoint_t* ep,
const void *data, int len)
Expand Down Expand Up @@ -844,8 +891,6 @@ static unsigned int snd_usbmidi_count_bits(unsigned int x)
*/
static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep)
{
if (ep->tasklet.func)
tasklet_kill(&ep->tasklet);
if (ep->urb) {
usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer,
ep->urb->transfer_buffer,
Expand Down Expand Up @@ -934,8 +979,11 @@ void snd_usbmidi_disconnect(struct list_head* p)
int i;

umidi = list_entry(p, snd_usb_midi_t, list);
del_timer_sync(&umidi->error_timer);
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i];
if (ep->out)
tasklet_kill(&ep->out->tasklet);
if (ep->out && ep->out->urb) {
usb_kill_urb(ep->out->urb);
if (umidi->usb_protocol_ops->finish_out_endpoint)
Expand Down Expand Up @@ -1496,6 +1544,9 @@ int snd_usb_create_midi_interface(snd_usb_audio_t* chip,
umidi->iface = iface;
umidi->quirk = quirk;
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
init_timer(&umidi->error_timer);
umidi->error_timer.function = snd_usbmidi_error_timer;
umidi->error_timer.data = (unsigned long)umidi;

/* detect the endpoint(s) to use */
memset(endpoints, 0, sizeof(endpoints));
Expand Down

0 comments on commit c884697

Please sign in to comment.