aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2005-08-02 09:26:52 -0400
committerJaroslav Kysela <perex@suse.cz>2005-08-30 07:14:13 -0400
commitc88469704d63787e8d44ca5ea1c1bd0adc29572d (patch)
treee898ee29e49ae3cef7440dfeb7f9972126b3fd06 /sound/usb
parentd568121ce3151c36cc4718dd4e977f217c6144c2 (diff)
[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>
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/usbmidi.c77
1 files changed, 64 insertions, 13 deletions
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 5f19b494923e..93dedde3c428 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -44,6 +44,7 @@
44#include <linux/string.h> 44#include <linux/string.h>
45#include <linux/init.h> 45#include <linux/init.h>
46#include <linux/slab.h> 46#include <linux/slab.h>
47#include <linux/timer.h>
47#include <linux/usb.h> 48#include <linux/usb.h>
48#include <sound/core.h> 49#include <sound/core.h>
49#include <sound/minors.h> 50#include <sound/minors.h>
@@ -56,6 +57,12 @@
56 */ 57 */
57/* #define DUMP_PACKETS */ 58/* #define DUMP_PACKETS */
58 59
60/*
61 * how long to wait after some USB errors, so that khubd can disconnect() us
62 * without too many spurious errors
63 */
64#define ERROR_DELAY_JIFFIES (HZ / 10)
65
59 66
60MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 67MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
61MODULE_DESCRIPTION("USB Audio/MIDI helper module"); 68MODULE_DESCRIPTION("USB Audio/MIDI helper module");
@@ -100,6 +107,7 @@ struct snd_usb_midi {
100 snd_rawmidi_t* rmidi; 107 snd_rawmidi_t* rmidi;
101 struct usb_protocol_ops* usb_protocol_ops; 108 struct usb_protocol_ops* usb_protocol_ops;
102 struct list_head list; 109 struct list_head list;
110 struct timer_list error_timer;
103 111
104 struct snd_usb_midi_endpoint { 112 struct snd_usb_midi_endpoint {
105 snd_usb_midi_out_endpoint_t *out; 113 snd_usb_midi_out_endpoint_t *out;
@@ -141,7 +149,8 @@ struct snd_usb_midi_in_endpoint {
141 struct usbmidi_in_port { 149 struct usbmidi_in_port {
142 snd_rawmidi_substream_t* substream; 150 snd_rawmidi_substream_t* substream;
143 } ports[0x10]; 151 } ports[0x10];
144 int seen_f5; 152 u8 seen_f5;
153 u8 error_resubmit;
145 int current_port; 154 int current_port;
146}; 155};
147 156
@@ -167,14 +176,22 @@ static int snd_usbmidi_submit_urb(struct urb* urb, int flags)
167 */ 176 */
168static int snd_usbmidi_urb_error(int status) 177static int snd_usbmidi_urb_error(int status)
169{ 178{
170 if (status == -ENOENT) 179 switch (status) {
171 return status; /* killed */ 180 /* manually unlinked, or device gone */
172 if (status == -EILSEQ || 181 case -ENOENT:
173 status == -ECONNRESET || 182 case -ECONNRESET:
174 status == -ETIMEDOUT) 183 case -ESHUTDOWN:
175 return -ENODEV; /* device removed/shutdown */ 184 case -ENODEV:
176 snd_printk(KERN_ERR "urb status %d\n", status); 185 return -ENODEV;
177 return 0; /* continue */ 186 /* errors that might occur during unplugging */
187 case -EPROTO: /* EHCI */
188 case -ETIMEDOUT: /* OHCI */
189 case -EILSEQ: /* UHCI */
190 return -EIO;
191 default:
192 snd_printk(KERN_ERR "urb status %d\n", status);
193 return 0; /* continue */
194 }
178} 195}
179 196
180/* 197/*
@@ -218,8 +235,15 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb, struct pt_regs *regs)
218 ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer, 235 ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer,
219 urb->actual_length); 236 urb->actual_length);
220 } else { 237 } else {
221 if (snd_usbmidi_urb_error(urb->status) < 0) 238 int err = snd_usbmidi_urb_error(urb->status);
239 if (err < 0) {
240 if (err != -ENODEV) {
241 ep->error_resubmit = 1;
242 mod_timer(&ep->umidi->error_timer,
243 jiffies + ERROR_DELAY_JIFFIES);
244 }
222 return; 245 return;
246 }
223 } 247 }
224 248
225 if (usb_pipe_needs_resubmit(urb->pipe)) { 249 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)
236 ep->urb_active = 0; 260 ep->urb_active = 0;
237 spin_unlock(&ep->buffer_lock); 261 spin_unlock(&ep->buffer_lock);
238 if (urb->status < 0) { 262 if (urb->status < 0) {
239 if (snd_usbmidi_urb_error(urb->status) < 0) 263 int err = snd_usbmidi_urb_error(urb->status);
264 if (err < 0) {
265 if (err != -ENODEV)
266 mod_timer(&ep->umidi->error_timer,
267 jiffies + ERROR_DELAY_JIFFIES);
240 return; 268 return;
269 }
241 } 270 }
242 snd_usbmidi_do_output(ep); 271 snd_usbmidi_do_output(ep);
243} 272}
@@ -276,6 +305,24 @@ static void snd_usbmidi_out_tasklet(unsigned long data)
276 snd_usbmidi_do_output(ep); 305 snd_usbmidi_do_output(ep);
277} 306}
278 307
308/* called after transfers had been interrupted due to some USB error */
309static void snd_usbmidi_error_timer(unsigned long data)
310{
311 snd_usb_midi_t *umidi = (snd_usb_midi_t *)data;
312 int i;
313
314 for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
315 snd_usb_midi_in_endpoint_t *in = umidi->endpoints[i].in;
316 if (in && in->error_resubmit) {
317 in->error_resubmit = 0;
318 in->urb->dev = umidi->chip->dev;
319 snd_usbmidi_submit_urb(in->urb, GFP_ATOMIC);
320 }
321 if (umidi->endpoints[i].out)
322 snd_usbmidi_do_output(umidi->endpoints[i].out);
323 }
324}
325
279/* helper function to send static data that may not DMA-able */ 326/* helper function to send static data that may not DMA-able */
280static int send_bulk_static_data(snd_usb_midi_out_endpoint_t* ep, 327static int send_bulk_static_data(snd_usb_midi_out_endpoint_t* ep,
281 const void *data, int len) 328 const void *data, int len)
@@ -844,8 +891,6 @@ static unsigned int snd_usbmidi_count_bits(unsigned int x)
844 */ 891 */
845static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep) 892static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep)
846{ 893{
847 if (ep->tasklet.func)
848 tasklet_kill(&ep->tasklet);
849 if (ep->urb) { 894 if (ep->urb) {
850 usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer, 895 usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer,
851 ep->urb->transfer_buffer, 896 ep->urb->transfer_buffer,
@@ -934,8 +979,11 @@ void snd_usbmidi_disconnect(struct list_head* p)
934 int i; 979 int i;
935 980
936 umidi = list_entry(p, snd_usb_midi_t, list); 981 umidi = list_entry(p, snd_usb_midi_t, list);
982 del_timer_sync(&umidi->error_timer);
937 for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { 983 for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
938 snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i]; 984 snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i];
985 if (ep->out)
986 tasklet_kill(&ep->out->tasklet);
939 if (ep->out && ep->out->urb) { 987 if (ep->out && ep->out->urb) {
940 usb_kill_urb(ep->out->urb); 988 usb_kill_urb(ep->out->urb);
941 if (umidi->usb_protocol_ops->finish_out_endpoint) 989 if (umidi->usb_protocol_ops->finish_out_endpoint)
@@ -1496,6 +1544,9 @@ int snd_usb_create_midi_interface(snd_usb_audio_t* chip,
1496 umidi->iface = iface; 1544 umidi->iface = iface;
1497 umidi->quirk = quirk; 1545 umidi->quirk = quirk;
1498 umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; 1546 umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
1547 init_timer(&umidi->error_timer);
1548 umidi->error_timer.function = snd_usbmidi_error_timer;
1549 umidi->error_timer.data = (unsigned long)umidi;
1499 1550
1500 /* detect the endpoint(s) to use */ 1551 /* detect the endpoint(s) to use */
1501 memset(endpoints, 0, sizeof(endpoints)); 1552 memset(endpoints, 0, sizeof(endpoints));