Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fd30afa4 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

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
parents b34c8663 f1e6d3c5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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) {
+194 −96
Original line number Diff line number Diff line
@@ -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 out_urb_context {
		struct urb *urb;
	int urb_active;
		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_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);
			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;
			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) {
	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);
	for (i = 0; i < INPUT_URBS; ++i) {
		buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL,
				  &ep->urb->transfer_dma);
					  &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->urb, umidi->chip->dev, pipe, buffer,
				 length, snd_usbmidi_in_urb_complete, ep,
				 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->urb, umidi->chip->dev, pipe, buffer,
				  length, snd_usbmidi_in_urb_complete, ep);
	ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
			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;
	}

	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) {
	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);
	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->urb, umidi->chip->dev, pipe, buffer,
				 ep->max_transfer, snd_usbmidi_out_urb_complete,
				 ep, 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->urb, umidi->chip->dev,
			usb_fill_bulk_urb(ep->urbs[i].urb, umidi->chip->dev,
					  pipe, buffer, ep->max_transfer,
				  snd_usbmidi_out_urb_complete, ep);
	ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
					  snd_usbmidi_out_urb_complete,
					  &ep->urbs[i]);
		ep->urbs[i].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);
	}
+60 −0
Original line number Diff line number Diff line
@@ -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;