aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/usb/usbmidi.c79
1 files changed, 60 insertions, 19 deletions
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 32d70cc046e7..0eff19ceb7e1 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -45,6 +45,7 @@
45#include <linux/slab.h> 45#include <linux/slab.h>
46#include <linux/timer.h> 46#include <linux/timer.h>
47#include <linux/usb.h> 47#include <linux/usb.h>
48#include <linux/wait.h>
48#include <sound/core.h> 49#include <sound/core.h>
49#include <sound/rawmidi.h> 50#include <sound/rawmidi.h>
50#include <sound/asequencer.h> 51#include <sound/asequencer.h>
@@ -124,9 +125,10 @@ struct snd_usb_midi_out_endpoint {
124 struct snd_usb_midi_out_endpoint *ep; 125 struct snd_usb_midi_out_endpoint *ep;
125 } urbs[OUTPUT_URBS]; 126 } urbs[OUTPUT_URBS];
126 unsigned int active_urbs; 127 unsigned int active_urbs;
128 unsigned int drain_urbs;
127 int max_transfer; /* size of urb buffer */ 129 int max_transfer; /* size of urb buffer */
128 struct tasklet_struct tasklet; 130 struct tasklet_struct tasklet;
129 131 unsigned int next_urb;
130 spinlock_t buffer_lock; 132 spinlock_t buffer_lock;
131 133
132 struct usbmidi_out_port { 134 struct usbmidi_out_port {
@@ -145,6 +147,8 @@ struct snd_usb_midi_out_endpoint {
145 uint8_t data[2]; 147 uint8_t data[2];
146 } ports[0x10]; 148 } ports[0x10];
147 int current_port; 149 int current_port;
150
151 wait_queue_head_t drain_wait;
148}; 152};
149 153
150struct snd_usb_midi_in_endpoint { 154struct snd_usb_midi_in_endpoint {
@@ -259,9 +263,15 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb)
259{ 263{
260 struct out_urb_context *context = urb->context; 264 struct out_urb_context *context = urb->context;
261 struct snd_usb_midi_out_endpoint* ep = context->ep; 265 struct snd_usb_midi_out_endpoint* ep = context->ep;
266 unsigned int urb_index;
262 267
263 spin_lock(&ep->buffer_lock); 268 spin_lock(&ep->buffer_lock);
264 ep->active_urbs &= ~(1 << (context - ep->urbs)); 269 urb_index = context - ep->urbs;
270 ep->active_urbs &= ~(1 << urb_index);
271 if (unlikely(ep->drain_urbs)) {
272 ep->drain_urbs &= ~(1 << urb_index);
273 wake_up(&ep->drain_wait);
274 }
265 spin_unlock(&ep->buffer_lock); 275 spin_unlock(&ep->buffer_lock);
266 if (urb->status < 0) { 276 if (urb->status < 0) {
267 int err = snd_usbmidi_urb_error(urb->status); 277 int err = snd_usbmidi_urb_error(urb->status);
@@ -291,28 +301,28 @@ static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep)
291 return; 301 return;
292 } 302 }
293 303
304 urb_index = ep->next_urb;
294 for (;;) { 305 for (;;) {
295 urb = NULL; 306 if (!(ep->active_urbs & (1 << urb_index))) {
296 for (urb_index = 0; urb_index < OUTPUT_URBS; ++urb_index) 307 urb = ep->urbs[urb_index].urb;
297 if (!(ep->active_urbs & (1 << urb_index))) { 308 urb->transfer_buffer_length = 0;
298 urb = ep->urbs[urb_index].urb; 309 ep->umidi->usb_protocol_ops->output(ep, urb);
310 if (urb->transfer_buffer_length == 0)
299 break; 311 break;
300 }
301 if (!urb)
302 break;
303
304 urb->transfer_buffer_length = 0;
305 ep->umidi->usb_protocol_ops->output(ep, urb);
306 if (urb->transfer_buffer_length == 0)
307 break;
308 312
309 dump_urb("sending", urb->transfer_buffer, 313 dump_urb("sending", urb->transfer_buffer,
310 urb->transfer_buffer_length); 314 urb->transfer_buffer_length);
311 urb->dev = ep->umidi->chip->dev; 315 urb->dev = ep->umidi->chip->dev;
312 if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0) 316 if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0)
317 break;
318 ep->active_urbs |= 1 << urb_index;
319 }
320 if (++urb_index >= OUTPUT_URBS)
321 urb_index = 0;
322 if (urb_index == ep->next_urb)
313 break; 323 break;
314 ep->active_urbs |= 1 << urb_index;
315 } 324 }
325 ep->next_urb = urb_index;
316 spin_unlock_irqrestore(&ep->buffer_lock, flags); 326 spin_unlock_irqrestore(&ep->buffer_lock, flags);
317} 327}
318 328
@@ -913,6 +923,35 @@ static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream,
913 } 923 }
914} 924}
915 925
926static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream)
927{
928 struct usbmidi_out_port* port = substream->runtime->private_data;
929 struct snd_usb_midi_out_endpoint *ep = port->ep;
930 unsigned int drain_urbs;
931 DEFINE_WAIT(wait);
932 long timeout = msecs_to_jiffies(50);
933
934 /*
935 * The substream buffer is empty, but some data might still be in the
936 * currently active URBs, so we have to wait for those to complete.
937 */
938 spin_lock_irq(&ep->buffer_lock);
939 drain_urbs = ep->active_urbs;
940 if (drain_urbs) {
941 ep->drain_urbs |= drain_urbs;
942 do {
943 prepare_to_wait(&ep->drain_wait, &wait,
944 TASK_UNINTERRUPTIBLE);
945 spin_unlock_irq(&ep->buffer_lock);
946 timeout = schedule_timeout(timeout);
947 spin_lock_irq(&ep->buffer_lock);
948 drain_urbs &= ep->drain_urbs;
949 } while (drain_urbs && timeout);
950 finish_wait(&ep->drain_wait, &wait);
951 }
952 spin_unlock_irq(&ep->buffer_lock);
953}
954
916static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream) 955static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)
917{ 956{
918 return 0; 957 return 0;
@@ -937,6 +976,7 @@ static struct snd_rawmidi_ops snd_usbmidi_output_ops = {
937 .open = snd_usbmidi_output_open, 976 .open = snd_usbmidi_output_open,
938 .close = snd_usbmidi_output_close, 977 .close = snd_usbmidi_output_close,
939 .trigger = snd_usbmidi_output_trigger, 978 .trigger = snd_usbmidi_output_trigger,
979 .drain = snd_usbmidi_output_drain,
940}; 980};
941 981
942static struct snd_rawmidi_ops snd_usbmidi_input_ops = { 982static struct snd_rawmidi_ops snd_usbmidi_input_ops = {
@@ -1103,6 +1143,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
1103 1143
1104 spin_lock_init(&ep->buffer_lock); 1144 spin_lock_init(&ep->buffer_lock);
1105 tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep); 1145 tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep);
1146 init_waitqueue_head(&ep->drain_wait);
1106 1147
1107 for (i = 0; i < 0x10; ++i) 1148 for (i = 0; i < 0x10; ++i)
1108 if (ep_info->out_cables & (1 << i)) { 1149 if (ep_info->out_cables & (1 << i)) {