[ALSA] usb-audio: throttle MIDI URB resubmits on USB errors
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>
This commit is contained in:
parent
d568121ce3
commit
c88469704d
|
@ -44,6 +44,7 @@
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/timer.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/minors.h>
|
#include <sound/minors.h>
|
||||||
|
@ -56,6 +57,12 @@
|
||||||
*/
|
*/
|
||||||
/* #define DUMP_PACKETS */
|
/* #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_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||||
MODULE_DESCRIPTION("USB Audio/MIDI helper module");
|
MODULE_DESCRIPTION("USB Audio/MIDI helper module");
|
||||||
|
@ -100,6 +107,7 @@ struct snd_usb_midi {
|
||||||
snd_rawmidi_t* rmidi;
|
snd_rawmidi_t* rmidi;
|
||||||
struct usb_protocol_ops* usb_protocol_ops;
|
struct usb_protocol_ops* usb_protocol_ops;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
struct timer_list error_timer;
|
||||||
|
|
||||||
struct snd_usb_midi_endpoint {
|
struct snd_usb_midi_endpoint {
|
||||||
snd_usb_midi_out_endpoint_t *out;
|
snd_usb_midi_out_endpoint_t *out;
|
||||||
|
@ -141,7 +149,8 @@ struct snd_usb_midi_in_endpoint {
|
||||||
struct usbmidi_in_port {
|
struct usbmidi_in_port {
|
||||||
snd_rawmidi_substream_t* substream;
|
snd_rawmidi_substream_t* substream;
|
||||||
} ports[0x10];
|
} ports[0x10];
|
||||||
int seen_f5;
|
u8 seen_f5;
|
||||||
|
u8 error_resubmit;
|
||||||
int current_port;
|
int current_port;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -167,14 +176,22 @@ static int snd_usbmidi_submit_urb(struct urb* urb, int flags)
|
||||||
*/
|
*/
|
||||||
static int snd_usbmidi_urb_error(int status)
|
static int snd_usbmidi_urb_error(int status)
|
||||||
{
|
{
|
||||||
if (status == -ENOENT)
|
switch (status) {
|
||||||
return status; /* killed */
|
/* manually unlinked, or device gone */
|
||||||
if (status == -EILSEQ ||
|
case -ENOENT:
|
||||||
status == -ECONNRESET ||
|
case -ECONNRESET:
|
||||||
status == -ETIMEDOUT)
|
case -ESHUTDOWN:
|
||||||
return -ENODEV; /* device removed/shutdown */
|
case -ENODEV:
|
||||||
snd_printk(KERN_ERR "urb status %d\n", status);
|
return -ENODEV;
|
||||||
return 0; /* continue */
|
/* 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 */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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,
|
ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer,
|
||||||
urb->actual_length);
|
urb->actual_length);
|
||||||
} else {
|
} 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;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usb_pipe_needs_resubmit(urb->pipe)) {
|
if (usb_pipe_needs_resubmit(urb->pipe)) {
|
||||||
|
@ -236,8 +260,13 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb, struct pt_regs *regs)
|
||||||
ep->urb_active = 0;
|
ep->urb_active = 0;
|
||||||
spin_unlock(&ep->buffer_lock);
|
spin_unlock(&ep->buffer_lock);
|
||||||
if (urb->status < 0) {
|
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;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
snd_usbmidi_do_output(ep);
|
snd_usbmidi_do_output(ep);
|
||||||
}
|
}
|
||||||
|
@ -276,6 +305,24 @@ static void snd_usbmidi_out_tasklet(unsigned long data)
|
||||||
snd_usbmidi_do_output(ep);
|
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 */
|
/* helper function to send static data that may not DMA-able */
|
||||||
static int send_bulk_static_data(snd_usb_midi_out_endpoint_t* ep,
|
static int send_bulk_static_data(snd_usb_midi_out_endpoint_t* ep,
|
||||||
const void *data, int len)
|
const void *data, int len)
|
||||||
|
@ -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)
|
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) {
|
if (ep->urb) {
|
||||||
usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer,
|
usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer,
|
||||||
ep->urb->transfer_buffer,
|
ep->urb->transfer_buffer,
|
||||||
|
@ -934,8 +979,11 @@ void snd_usbmidi_disconnect(struct list_head* p)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
umidi = list_entry(p, snd_usb_midi_t, list);
|
umidi = list_entry(p, snd_usb_midi_t, list);
|
||||||
|
del_timer_sync(&umidi->error_timer);
|
||||||
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
|
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
|
||||||
snd_usb_midi_endpoint_t* ep = &umidi->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) {
|
if (ep->out && ep->out->urb) {
|
||||||
usb_kill_urb(ep->out->urb);
|
usb_kill_urb(ep->out->urb);
|
||||||
if (umidi->usb_protocol_ops->finish_out_endpoint)
|
if (umidi->usb_protocol_ops->finish_out_endpoint)
|
||||||
|
@ -1496,6 +1544,9 @@ int snd_usb_create_midi_interface(snd_usb_audio_t* chip,
|
||||||
umidi->iface = iface;
|
umidi->iface = iface;
|
||||||
umidi->quirk = quirk;
|
umidi->quirk = quirk;
|
||||||
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
|
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 */
|
/* detect the endpoint(s) to use */
|
||||||
memset(endpoints, 0, sizeof(endpoints));
|
memset(endpoints, 0, sizeof(endpoints));
|
||||||
|
|
Loading…
Reference in a new issue