diff options
| author | Takashi Iwai <tiwai@suse.de> | 2009-09-10 15:33:07 +0200 | 
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2009-09-10 15:33:07 +0200 | 
| commit | fd30afa454282bbe1b36d5d77bd72c0ea5b3f97c (patch) | |
| tree | 37557d4c96d89641eef362995322547b91b24669 | |
| parent | b34c8663940dfc7da767c42b37605e5ad3ae11f7 (diff) | |
| parent | f1e6d3c5cf86675e54a17a89668aa6685d2ef59d (diff) | |
| download | olio-linux-3.10-fd30afa454282bbe1b36d5d77bd72c0ea5b3f97c.tar.xz olio-linux-3.10-fd30afa454282bbe1b36d5d77bd72c0ea5b3f97c.zip  | |
Merge branch 'topic/usb-audio' into for-linus
* topic/usb-audio:
  ALSA: usb-audio - Fix types taken in min()
  sound: usb-audio: do not make URBs longer than sync packet interval
  sound: usb-audio: add MIDI drain callback
  sound: usb-audio: use multiple output URBs
  sound: usb-audio: use multiple input URBs
  sound: usb-audio: Xonar U1 digital output support
| -rw-r--r-- | sound/usb/usbaudio.c | 2 | ||||
| -rw-r--r-- | sound/usb/usbmidi.c | 290 | ||||
| -rw-r--r-- | sound/usb/usbmixer.c | 60 | 
3 files changed, 256 insertions, 96 deletions
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 3a53c79f48b..8db0374e10d 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -1083,6 +1083,8 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri  	} else  		urb_packs = 1;  	urb_packs *= packs_per_ms; +	if (subs->syncpipe) +		urb_packs = min(urb_packs, 1U << subs->syncinterval);  	/* decide how many packets to be used */  	if (is_playback) { diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 2fb35cc22a3..0eff19ceb7e 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -45,6 +45,7 @@  #include <linux/slab.h>  #include <linux/timer.h>  #include <linux/usb.h> +#include <linux/wait.h>  #include <sound/core.h>  #include <sound/rawmidi.h>  #include <sound/asequencer.h> @@ -62,6 +63,9 @@   */  #define ERROR_DELAY_JIFFIES (HZ / 10) +#define OUTPUT_URBS 7 +#define INPUT_URBS 7 +  MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");  MODULE_DESCRIPTION("USB Audio/MIDI helper module"); @@ -90,7 +94,7 @@ struct snd_usb_midi_endpoint;  struct usb_protocol_ops {  	void (*input)(struct snd_usb_midi_in_endpoint*, uint8_t*, int); -	void (*output)(struct snd_usb_midi_out_endpoint*); +	void (*output)(struct snd_usb_midi_out_endpoint *ep, struct urb *urb);  	void (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t);  	void (*init_out_endpoint)(struct snd_usb_midi_out_endpoint*);  	void (*finish_out_endpoint)(struct snd_usb_midi_out_endpoint*); @@ -116,11 +120,15 @@ struct snd_usb_midi {  struct snd_usb_midi_out_endpoint {  	struct snd_usb_midi* umidi; -	struct urb* urb; -	int urb_active; +	struct out_urb_context { +		struct urb *urb; +		struct snd_usb_midi_out_endpoint *ep; +	} urbs[OUTPUT_URBS]; +	unsigned int active_urbs; +	unsigned int drain_urbs;  	int max_transfer;		/* size of urb buffer */  	struct tasklet_struct tasklet; - +	unsigned int next_urb;  	spinlock_t buffer_lock;  	struct usbmidi_out_port { @@ -139,11 +147,13 @@ struct snd_usb_midi_out_endpoint {  		uint8_t data[2];  	} ports[0x10];  	int current_port; + +	wait_queue_head_t drain_wait;  };  struct snd_usb_midi_in_endpoint {  	struct snd_usb_midi* umidi; -	struct urb* urb; +	struct urb* urbs[INPUT_URBS];  	struct usbmidi_in_port {  		struct snd_rawmidi_substream *substream;  		u8 running_status_length; @@ -251,10 +261,17 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb)  static void snd_usbmidi_out_urb_complete(struct urb* urb)  { -	struct snd_usb_midi_out_endpoint* ep = urb->context; +	struct out_urb_context *context = urb->context; +	struct snd_usb_midi_out_endpoint* ep = context->ep; +	unsigned int urb_index;  	spin_lock(&ep->buffer_lock); -	ep->urb_active = 0; +	urb_index = context - ep->urbs; +	ep->active_urbs &= ~(1 << urb_index); +	if (unlikely(ep->drain_urbs)) { +		ep->drain_urbs &= ~(1 << urb_index); +		wake_up(&ep->drain_wait); +	}  	spin_unlock(&ep->buffer_lock);  	if (urb->status < 0) {  		int err = snd_usbmidi_urb_error(urb->status); @@ -274,24 +291,38 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb)   */  static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep)  { -	struct urb* urb = ep->urb; +	unsigned int urb_index; +	struct urb* urb;  	unsigned long flags;  	spin_lock_irqsave(&ep->buffer_lock, flags); -	if (ep->urb_active || ep->umidi->chip->shutdown) { +	if (ep->umidi->chip->shutdown) {  		spin_unlock_irqrestore(&ep->buffer_lock, flags);  		return;  	} -	urb->transfer_buffer_length = 0; -	ep->umidi->usb_protocol_ops->output(ep); +	urb_index = ep->next_urb; +	for (;;) { +		if (!(ep->active_urbs & (1 << urb_index))) { +			urb = ep->urbs[urb_index].urb; +			urb->transfer_buffer_length = 0; +			ep->umidi->usb_protocol_ops->output(ep, urb); +			if (urb->transfer_buffer_length == 0) +				break; -	if (urb->transfer_buffer_length > 0) { -		dump_urb("sending", urb->transfer_buffer, -			 urb->transfer_buffer_length); -		urb->dev = ep->umidi->chip->dev; -		ep->urb_active = snd_usbmidi_submit_urb(urb, GFP_ATOMIC) >= 0; +			dump_urb("sending", urb->transfer_buffer, +				 urb->transfer_buffer_length); +			urb->dev = ep->umidi->chip->dev; +			if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0) +				break; +			ep->active_urbs |= 1 << urb_index; +		} +		if (++urb_index >= OUTPUT_URBS) +			urb_index = 0; +		if (urb_index == ep->next_urb) +			break;  	} +	ep->next_urb = urb_index;  	spin_unlock_irqrestore(&ep->buffer_lock, flags);  } @@ -306,7 +337,7 @@ static void snd_usbmidi_out_tasklet(unsigned long data)  static void snd_usbmidi_error_timer(unsigned long data)  {  	struct snd_usb_midi *umidi = (struct snd_usb_midi *)data; -	int i; +	unsigned int i, j;  	spin_lock(&umidi->disc_lock);  	if (umidi->disconnected) { @@ -317,8 +348,10 @@ static void snd_usbmidi_error_timer(unsigned long data)  		struct snd_usb_midi_in_endpoint *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); +			for (j = 0; j < INPUT_URBS; ++j) { +				in->urbs[j]->dev = umidi->chip->dev; +				snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC); +			}  		}  		if (umidi->endpoints[i].out)  			snd_usbmidi_do_output(umidi->endpoints[i].out); @@ -330,13 +363,14 @@ static void snd_usbmidi_error_timer(unsigned long data)  static int send_bulk_static_data(struct snd_usb_midi_out_endpoint* ep,  				 const void *data, int len)  { -	int err; +	int err = 0;  	void *buf = kmemdup(data, len, GFP_KERNEL);  	if (!buf)  		return -ENOMEM;  	dump_urb("sending", buf, len); -	err = usb_bulk_msg(ep->umidi->chip->dev, ep->urb->pipe, buf, len, -			   NULL, 250); +	if (ep->urbs[0].urb) +		err = usb_bulk_msg(ep->umidi->chip->dev, ep->urbs[0].urb->pipe, +				   buf, len, NULL, 250);  	kfree(buf);  	return err;  } @@ -554,9 +588,9 @@ static void snd_usbmidi_transmit_byte(struct usbmidi_out_port* port,  	}  } -static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint* ep) +static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint* ep, +					struct urb *urb)  { -	struct urb* urb = ep->urb;  	int p;  	/* FIXME: lower-numbered ports can starve higher-numbered ports */ @@ -613,14 +647,15 @@ static void snd_usbmidi_novation_input(struct snd_usb_midi_in_endpoint* ep,  	snd_usbmidi_input_data(ep, 0, &buffer[2], buffer[0] - 1);  } -static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint* ep) +static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint* ep, +					struct urb *urb)  {  	uint8_t* transfer_buffer;  	int count;  	if (!ep->ports[0].active)  		return; -	transfer_buffer = ep->urb->transfer_buffer; +	transfer_buffer = urb->transfer_buffer;  	count = snd_rawmidi_transmit(ep->ports[0].substream,  				     &transfer_buffer[2],  				     ep->max_transfer - 2); @@ -630,7 +665,7 @@ static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint* ep)  	}  	transfer_buffer[0] = 0;  	transfer_buffer[1] = count; -	ep->urb->transfer_buffer_length = 2 + count; +	urb->transfer_buffer_length = 2 + count;  }  static struct usb_protocol_ops snd_usbmidi_novation_ops = { @@ -648,20 +683,21 @@ static void snd_usbmidi_raw_input(struct snd_usb_midi_in_endpoint* ep,  	snd_usbmidi_input_data(ep, 0, buffer, buffer_length);  } -static void snd_usbmidi_raw_output(struct snd_usb_midi_out_endpoint* ep) +static void snd_usbmidi_raw_output(struct snd_usb_midi_out_endpoint* ep, +				   struct urb *urb)  {  	int count;  	if (!ep->ports[0].active)  		return;  	count = snd_rawmidi_transmit(ep->ports[0].substream, -				     ep->urb->transfer_buffer, +				     urb->transfer_buffer,  				     ep->max_transfer);  	if (count < 1) {  		ep->ports[0].active = 0;  		return;  	} -	ep->urb->transfer_buffer_length = count; +	urb->transfer_buffer_length = count;  }  static struct usb_protocol_ops snd_usbmidi_raw_ops = { @@ -681,23 +717,25 @@ static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep,  		snd_usbmidi_input_data(ep, 0, buffer, buffer_length);  } -static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep) +static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep, +				      struct urb *urb)  {  	int count;  	if (!ep->ports[0].active)  		return; -	count = ep->urb->dev->speed == USB_SPEED_HIGH ? 1 : 2; +	count = snd_usb_get_speed(ep->umidi->chip->dev) == USB_SPEED_HIGH +		? 1 : 2;  	count = snd_rawmidi_transmit(ep->ports[0].substream, -				     ep->urb->transfer_buffer, +				     urb->transfer_buffer,  				     count);  	if (count < 1) {  		ep->ports[0].active = 0;  		return;  	} -	memset(ep->urb->transfer_buffer + count, 0xFD, 9 - count); -	ep->urb->transfer_buffer_length = count; +	memset(urb->transfer_buffer + count, 0xFD, 9 - count); +	urb->transfer_buffer_length = count;  }  static struct usb_protocol_ops snd_usbmidi_122l_ops = { @@ -786,10 +824,11 @@ static void snd_usbmidi_emagic_input(struct snd_usb_midi_in_endpoint* ep,  	}  } -static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep) +static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep, +				      struct urb *urb)  {  	int port0 = ep->current_port; -	uint8_t* buf = ep->urb->transfer_buffer; +	uint8_t* buf = urb->transfer_buffer;  	int buf_free = ep->max_transfer;  	int length, i; @@ -829,7 +868,7 @@ static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep)  		*buf = 0xff;  		--buf_free;  	} -	ep->urb->transfer_buffer_length = ep->max_transfer - buf_free; +	urb->transfer_buffer_length = ep->max_transfer - buf_free;  }  static struct usb_protocol_ops snd_usbmidi_emagic_ops = { @@ -884,6 +923,35 @@ static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream,  	}  } +static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream) +{ +	struct usbmidi_out_port* port = substream->runtime->private_data; +	struct snd_usb_midi_out_endpoint *ep = port->ep; +	unsigned int drain_urbs; +	DEFINE_WAIT(wait); +	long timeout = msecs_to_jiffies(50); + +	/* +	 * The substream buffer is empty, but some data might still be in the +	 * currently active URBs, so we have to wait for those to complete. +	 */ +	spin_lock_irq(&ep->buffer_lock); +	drain_urbs = ep->active_urbs; +	if (drain_urbs) { +		ep->drain_urbs |= drain_urbs; +		do { +			prepare_to_wait(&ep->drain_wait, &wait, +					TASK_UNINTERRUPTIBLE); +			spin_unlock_irq(&ep->buffer_lock); +			timeout = schedule_timeout(timeout); +			spin_lock_irq(&ep->buffer_lock); +			drain_urbs &= ep->drain_urbs; +		} while (drain_urbs && timeout); +		finish_wait(&ep->drain_wait, &wait); +	} +	spin_unlock_irq(&ep->buffer_lock); +} +  static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)  {  	return 0; @@ -908,6 +976,7 @@ static struct snd_rawmidi_ops snd_usbmidi_output_ops = {  	.open = snd_usbmidi_output_open,  	.close = snd_usbmidi_output_close,  	.trigger = snd_usbmidi_output_trigger, +	.drain = snd_usbmidi_output_drain,  };  static struct snd_rawmidi_ops snd_usbmidi_input_ops = { @@ -916,19 +985,26 @@ static struct snd_rawmidi_ops snd_usbmidi_input_ops = {  	.trigger = snd_usbmidi_input_trigger  }; +static void free_urb_and_buffer(struct snd_usb_midi *umidi, struct urb *urb, +				unsigned int buffer_length) +{ +	usb_buffer_free(umidi->chip->dev, buffer_length, +			urb->transfer_buffer, urb->transfer_dma); +	usb_free_urb(urb); +} +  /*   * Frees an input endpoint.   * May be called when ep hasn't been initialized completely.   */  static void snd_usbmidi_in_endpoint_delete(struct snd_usb_midi_in_endpoint* ep)  { -	if (ep->urb) { -		usb_buffer_free(ep->umidi->chip->dev, -				ep->urb->transfer_buffer_length, -				ep->urb->transfer_buffer, -				ep->urb->transfer_dma); -		usb_free_urb(ep->urb); -	} +	unsigned int i; + +	for (i = 0; i < INPUT_URBS; ++i) +		if (ep->urbs[i]) +			free_urb_and_buffer(ep->umidi, ep->urbs[i], +					    ep->urbs[i]->transfer_buffer_length);  	kfree(ep);  } @@ -943,6 +1019,7 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,  	void* buffer;  	unsigned int pipe;  	int length; +	unsigned int i;  	rep->in = NULL;  	ep = kzalloc(sizeof(*ep), GFP_KERNEL); @@ -950,30 +1027,36 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,  		return -ENOMEM;  	ep->umidi = umidi; -	ep->urb = usb_alloc_urb(0, GFP_KERNEL); -	if (!ep->urb) { -		snd_usbmidi_in_endpoint_delete(ep); -		return -ENOMEM; +	for (i = 0; i < INPUT_URBS; ++i) { +		ep->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); +		if (!ep->urbs[i]) { +			snd_usbmidi_in_endpoint_delete(ep); +			return -ENOMEM; +		}  	}  	if (ep_info->in_interval)  		pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->in_ep);  	else  		pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep);  	length = usb_maxpacket(umidi->chip->dev, pipe, 0); -	buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL, -				  &ep->urb->transfer_dma); -	if (!buffer) { -		snd_usbmidi_in_endpoint_delete(ep); -		return -ENOMEM; +	for (i = 0; i < INPUT_URBS; ++i) { +		buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL, +					  &ep->urbs[i]->transfer_dma); +		if (!buffer) { +			snd_usbmidi_in_endpoint_delete(ep); +			return -ENOMEM; +		} +		if (ep_info->in_interval) +			usb_fill_int_urb(ep->urbs[i], umidi->chip->dev, +					 pipe, buffer, length, +					 snd_usbmidi_in_urb_complete, +					 ep, ep_info->in_interval); +		else +			usb_fill_bulk_urb(ep->urbs[i], umidi->chip->dev, +					  pipe, buffer, length, +					  snd_usbmidi_in_urb_complete, ep); +		ep->urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;  	} -	if (ep_info->in_interval) -		usb_fill_int_urb(ep->urb, umidi->chip->dev, pipe, buffer, -				 length, snd_usbmidi_in_urb_complete, ep, -				 ep_info->in_interval); -	else -		usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer, -				  length, snd_usbmidi_in_urb_complete, ep); -	ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;  	rep->in = ep;  	return 0; @@ -994,12 +1077,12 @@ static unsigned int snd_usbmidi_count_bits(unsigned int x)   */  static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint* ep)  { -	if (ep->urb) { -		usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer, -				ep->urb->transfer_buffer, -				ep->urb->transfer_dma); -		usb_free_urb(ep->urb); -	} +	unsigned int i; + +	for (i = 0; i < OUTPUT_URBS; ++i) +		if (ep->urbs[i].urb) +			free_urb_and_buffer(ep->umidi, ep->urbs[i].urb, +					    ep->max_transfer);  	kfree(ep);  } @@ -1011,7 +1094,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,  			 		   struct snd_usb_midi_endpoint* rep)  {  	struct snd_usb_midi_out_endpoint* ep; -	int i; +	unsigned int i;  	unsigned int pipe;  	void* buffer; @@ -1021,38 +1104,46 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,  		return -ENOMEM;  	ep->umidi = umidi; -	ep->urb = usb_alloc_urb(0, GFP_KERNEL); -	if (!ep->urb) { -		snd_usbmidi_out_endpoint_delete(ep); -		return -ENOMEM; +	for (i = 0; i < OUTPUT_URBS; ++i) { +		ep->urbs[i].urb = usb_alloc_urb(0, GFP_KERNEL); +		if (!ep->urbs[i].urb) { +			snd_usbmidi_out_endpoint_delete(ep); +			return -ENOMEM; +		} +		ep->urbs[i].ep = ep;  	}  	if (ep_info->out_interval)  		pipe = usb_sndintpipe(umidi->chip->dev, ep_info->out_ep);  	else  		pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep);  	if (umidi->chip->usb_id == USB_ID(0x0a92, 0x1020)) /* ESI M4U */ -		/* FIXME: we need more URBs to get reasonable bandwidth here: */  		ep->max_transfer = 4;  	else  		ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1); -	buffer = usb_buffer_alloc(umidi->chip->dev, ep->max_transfer, -				  GFP_KERNEL, &ep->urb->transfer_dma); -	if (!buffer) { -		snd_usbmidi_out_endpoint_delete(ep); -		return -ENOMEM; +	for (i = 0; i < OUTPUT_URBS; ++i) { +		buffer = usb_buffer_alloc(umidi->chip->dev, +					  ep->max_transfer, GFP_KERNEL, +					  &ep->urbs[i].urb->transfer_dma); +		if (!buffer) { +			snd_usbmidi_out_endpoint_delete(ep); +			return -ENOMEM; +		} +		if (ep_info->out_interval) +			usb_fill_int_urb(ep->urbs[i].urb, umidi->chip->dev, +					 pipe, buffer, ep->max_transfer, +					 snd_usbmidi_out_urb_complete, +					 &ep->urbs[i], ep_info->out_interval); +		else +			usb_fill_bulk_urb(ep->urbs[i].urb, umidi->chip->dev, +					  pipe, buffer, ep->max_transfer, +					  snd_usbmidi_out_urb_complete, +					  &ep->urbs[i]); +		ep->urbs[i].urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;  	} -	if (ep_info->out_interval) -		usb_fill_int_urb(ep->urb, umidi->chip->dev, pipe, buffer, -				 ep->max_transfer, snd_usbmidi_out_urb_complete, -				 ep, ep_info->out_interval); -	else -		usb_fill_bulk_urb(ep->urb, umidi->chip->dev, -				  pipe, buffer, ep->max_transfer, -				  snd_usbmidi_out_urb_complete, ep); -	ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;  	spin_lock_init(&ep->buffer_lock);  	tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep); +	init_waitqueue_head(&ep->drain_wait);  	for (i = 0; i < 0x10; ++i)  		if (ep_info->out_cables & (1 << i)) { @@ -1090,7 +1181,7 @@ static void snd_usbmidi_free(struct snd_usb_midi* umidi)  void snd_usbmidi_disconnect(struct list_head* p)  {  	struct snd_usb_midi* umidi; -	int i; +	unsigned int i, j;  	umidi = list_entry(p, struct snd_usb_midi, list);  	/* @@ -1105,13 +1196,15 @@ void snd_usbmidi_disconnect(struct list_head* p)  		struct snd_usb_midi_endpoint* 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 (ep->out) { +			for (j = 0; j < OUTPUT_URBS; ++j) +				usb_kill_urb(ep->out->urbs[j].urb);  			if (umidi->usb_protocol_ops->finish_out_endpoint)  				umidi->usb_protocol_ops->finish_out_endpoint(ep->out);  		}  		if (ep->in) -			usb_kill_urb(ep->in->urb); +			for (j = 0; j < INPUT_URBS; ++j) +				usb_kill_urb(ep->in->urbs[j]);  		/* free endpoints here; later call can result in Oops */  		if (ep->out) {  			snd_usbmidi_out_endpoint_delete(ep->out); @@ -1692,20 +1785,25 @@ static int snd_usbmidi_create_rawmidi(struct snd_usb_midi* umidi,  void snd_usbmidi_input_stop(struct list_head* p)  {  	struct snd_usb_midi* umidi; -	int i; +	unsigned int i, j;  	umidi = list_entry(p, struct snd_usb_midi, list);  	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {  		struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];  		if (ep->in) -			usb_kill_urb(ep->in->urb); +			for (j = 0; j < INPUT_URBS; ++j) +				usb_kill_urb(ep->in->urbs[j]);  	}  }  static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)  { -	if (ep) { -		struct urb* urb = ep->urb; +	unsigned int i; + +	if (!ep) +		return; +	for (i = 0; i < INPUT_URBS; ++i) { +		struct urb* urb = ep->urbs[i];  		urb->dev = ep->umidi->chip->dev;  		snd_usbmidi_submit_urb(urb, GFP_KERNEL);  	} diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index df8969188d1..ab5a3ac2ac4 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -86,6 +86,7 @@ struct usb_mixer_interface {  	u8 rc_buffer[6];  	u8 audigy2nx_leds[3]; +	u8 xonar_u1_status;  }; @@ -2042,6 +2043,58 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,  	}  } +static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, +				   struct snd_ctl_elem_value *ucontrol) +{ +	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + +	ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02); +	return 0; +} + +static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol, +				   struct snd_ctl_elem_value *ucontrol) +{ +	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); +	u8 old_status, new_status; +	int err, changed; + +	old_status = mixer->xonar_u1_status; +	if (ucontrol->value.integer.value[0]) +		new_status = old_status | 0x02; +	else +		new_status = old_status & ~0x02; +	changed = new_status != old_status; +	err = snd_usb_ctl_msg(mixer->chip->dev, +			      usb_sndctrlpipe(mixer->chip->dev, 0), 0x08, +			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, +			      50, 0, &new_status, 1, 100); +	if (err < 0) +		return err; +	mixer->xonar_u1_status = new_status; +	return changed; +} + +static struct snd_kcontrol_new snd_xonar_u1_output_switch = { +	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	.name = "Digital Playback Switch", +	.info = snd_ctl_boolean_mono_info, +	.get = snd_xonar_u1_switch_get, +	.put = snd_xonar_u1_switch_put, +}; + +static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) +{ +	int err; + +	err = snd_ctl_add(mixer->chip->card, +			  snd_ctl_new1(&snd_xonar_u1_output_switch, mixer)); +	if (err < 0) +		return err; +	mixer->xonar_u1_status = 0x05; +	return 0; +} +  int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,  			 int ignore_error)  { @@ -2084,6 +2137,13 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,  					      snd_audigy2nx_proc_read);  	} +	if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) || +	    mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) { +		err = snd_xonar_u1_controls_create(mixer); +		if (err < 0) +			goto _error; +	} +  	err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);  	if (err < 0)  		goto _error;  |