mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 19:56:18 +00:00
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
This commit is contained in:
commit
fd30afa454
3 changed files with 256 additions and 96 deletions
|
@ -1083,6 +1083,8 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
|
||||||
} else
|
} else
|
||||||
urb_packs = 1;
|
urb_packs = 1;
|
||||||
urb_packs *= packs_per_ms;
|
urb_packs *= packs_per_ms;
|
||||||
|
if (subs->syncpipe)
|
||||||
|
urb_packs = min(urb_packs, 1U << subs->syncinterval);
|
||||||
|
|
||||||
/* decide how many packets to be used */
|
/* decide how many packets to be used */
|
||||||
if (is_playback) {
|
if (is_playback) {
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
#include <linux/wait.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/rawmidi.h>
|
#include <sound/rawmidi.h>
|
||||||
#include <sound/asequencer.h>
|
#include <sound/asequencer.h>
|
||||||
|
@ -62,6 +63,9 @@
|
||||||
*/
|
*/
|
||||||
#define ERROR_DELAY_JIFFIES (HZ / 10)
|
#define ERROR_DELAY_JIFFIES (HZ / 10)
|
||||||
|
|
||||||
|
#define OUTPUT_URBS 7
|
||||||
|
#define INPUT_URBS 7
|
||||||
|
|
||||||
|
|
||||||
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");
|
||||||
|
@ -90,7 +94,7 @@ struct snd_usb_midi_endpoint;
|
||||||
|
|
||||||
struct usb_protocol_ops {
|
struct usb_protocol_ops {
|
||||||
void (*input)(struct snd_usb_midi_in_endpoint*, uint8_t*, int);
|
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 (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t);
|
||||||
void (*init_out_endpoint)(struct snd_usb_midi_out_endpoint*);
|
void (*init_out_endpoint)(struct snd_usb_midi_out_endpoint*);
|
||||||
void (*finish_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_out_endpoint {
|
||||||
struct snd_usb_midi* umidi;
|
struct snd_usb_midi* umidi;
|
||||||
struct urb* urb;
|
struct out_urb_context {
|
||||||
int urb_active;
|
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 */
|
int max_transfer; /* size of urb buffer */
|
||||||
struct tasklet_struct tasklet;
|
struct tasklet_struct tasklet;
|
||||||
|
unsigned int next_urb;
|
||||||
spinlock_t buffer_lock;
|
spinlock_t buffer_lock;
|
||||||
|
|
||||||
struct usbmidi_out_port {
|
struct usbmidi_out_port {
|
||||||
|
@ -139,11 +147,13 @@ struct snd_usb_midi_out_endpoint {
|
||||||
uint8_t data[2];
|
uint8_t data[2];
|
||||||
} ports[0x10];
|
} ports[0x10];
|
||||||
int current_port;
|
int current_port;
|
||||||
|
|
||||||
|
wait_queue_head_t drain_wait;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct snd_usb_midi_in_endpoint {
|
struct snd_usb_midi_in_endpoint {
|
||||||
struct snd_usb_midi* umidi;
|
struct snd_usb_midi* umidi;
|
||||||
struct urb* urb;
|
struct urb* urbs[INPUT_URBS];
|
||||||
struct usbmidi_in_port {
|
struct usbmidi_in_port {
|
||||||
struct snd_rawmidi_substream *substream;
|
struct snd_rawmidi_substream *substream;
|
||||||
u8 running_status_length;
|
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)
|
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);
|
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);
|
spin_unlock(&ep->buffer_lock);
|
||||||
if (urb->status < 0) {
|
if (urb->status < 0) {
|
||||||
int err = snd_usbmidi_urb_error(urb->status);
|
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)
|
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;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&ep->buffer_lock, 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);
|
spin_unlock_irqrestore(&ep->buffer_lock, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
urb->transfer_buffer_length = 0;
|
urb_index = ep->next_urb;
|
||||||
ep->umidi->usb_protocol_ops->output(ep);
|
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,
|
||||||
dump_urb("sending", urb->transfer_buffer,
|
urb->transfer_buffer_length);
|
||||||
urb->transfer_buffer_length);
|
urb->dev = ep->umidi->chip->dev;
|
||||||
urb->dev = ep->umidi->chip->dev;
|
if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0)
|
||||||
ep->urb_active = 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);
|
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)
|
static void snd_usbmidi_error_timer(unsigned long data)
|
||||||
{
|
{
|
||||||
struct snd_usb_midi *umidi = (struct snd_usb_midi *)data;
|
struct snd_usb_midi *umidi = (struct snd_usb_midi *)data;
|
||||||
int i;
|
unsigned int i, j;
|
||||||
|
|
||||||
spin_lock(&umidi->disc_lock);
|
spin_lock(&umidi->disc_lock);
|
||||||
if (umidi->disconnected) {
|
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;
|
struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in;
|
||||||
if (in && in->error_resubmit) {
|
if (in && in->error_resubmit) {
|
||||||
in->error_resubmit = 0;
|
in->error_resubmit = 0;
|
||||||
in->urb->dev = umidi->chip->dev;
|
for (j = 0; j < INPUT_URBS; ++j) {
|
||||||
snd_usbmidi_submit_urb(in->urb, GFP_ATOMIC);
|
in->urbs[j]->dev = umidi->chip->dev;
|
||||||
|
snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (umidi->endpoints[i].out)
|
if (umidi->endpoints[i].out)
|
||||||
snd_usbmidi_do_output(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,
|
static int send_bulk_static_data(struct snd_usb_midi_out_endpoint* ep,
|
||||||
const void *data, int len)
|
const void *data, int len)
|
||||||
{
|
{
|
||||||
int err;
|
int err = 0;
|
||||||
void *buf = kmemdup(data, len, GFP_KERNEL);
|
void *buf = kmemdup(data, len, GFP_KERNEL);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
dump_urb("sending", buf, len);
|
dump_urb("sending", buf, len);
|
||||||
err = usb_bulk_msg(ep->umidi->chip->dev, ep->urb->pipe, buf, len,
|
if (ep->urbs[0].urb)
|
||||||
NULL, 250);
|
err = usb_bulk_msg(ep->umidi->chip->dev, ep->urbs[0].urb->pipe,
|
||||||
|
buf, len, NULL, 250);
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return err;
|
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;
|
int p;
|
||||||
|
|
||||||
/* FIXME: lower-numbered ports can starve higher-numbered ports */
|
/* 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);
|
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;
|
uint8_t* transfer_buffer;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
if (!ep->ports[0].active)
|
if (!ep->ports[0].active)
|
||||||
return;
|
return;
|
||||||
transfer_buffer = ep->urb->transfer_buffer;
|
transfer_buffer = urb->transfer_buffer;
|
||||||
count = snd_rawmidi_transmit(ep->ports[0].substream,
|
count = snd_rawmidi_transmit(ep->ports[0].substream,
|
||||||
&transfer_buffer[2],
|
&transfer_buffer[2],
|
||||||
ep->max_transfer - 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[0] = 0;
|
||||||
transfer_buffer[1] = count;
|
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 = {
|
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);
|
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;
|
int count;
|
||||||
|
|
||||||
if (!ep->ports[0].active)
|
if (!ep->ports[0].active)
|
||||||
return;
|
return;
|
||||||
count = snd_rawmidi_transmit(ep->ports[0].substream,
|
count = snd_rawmidi_transmit(ep->ports[0].substream,
|
||||||
ep->urb->transfer_buffer,
|
urb->transfer_buffer,
|
||||||
ep->max_transfer);
|
ep->max_transfer);
|
||||||
if (count < 1) {
|
if (count < 1) {
|
||||||
ep->ports[0].active = 0;
|
ep->ports[0].active = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ep->urb->transfer_buffer_length = count;
|
urb->transfer_buffer_length = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct usb_protocol_ops snd_usbmidi_raw_ops = {
|
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);
|
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;
|
int count;
|
||||||
|
|
||||||
if (!ep->ports[0].active)
|
if (!ep->ports[0].active)
|
||||||
return;
|
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,
|
count = snd_rawmidi_transmit(ep->ports[0].substream,
|
||||||
ep->urb->transfer_buffer,
|
urb->transfer_buffer,
|
||||||
count);
|
count);
|
||||||
if (count < 1) {
|
if (count < 1) {
|
||||||
ep->ports[0].active = 0;
|
ep->ports[0].active = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(ep->urb->transfer_buffer + count, 0xFD, 9 - count);
|
memset(urb->transfer_buffer + count, 0xFD, 9 - count);
|
||||||
ep->urb->transfer_buffer_length = count;
|
urb->transfer_buffer_length = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct usb_protocol_ops snd_usbmidi_122l_ops = {
|
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;
|
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 buf_free = ep->max_transfer;
|
||||||
int length, i;
|
int length, i;
|
||||||
|
|
||||||
|
@ -829,7 +868,7 @@ static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep)
|
||||||
*buf = 0xff;
|
*buf = 0xff;
|
||||||
--buf_free;
|
--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 = {
|
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)
|
static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -908,6 +976,7 @@ static struct snd_rawmidi_ops snd_usbmidi_output_ops = {
|
||||||
.open = snd_usbmidi_output_open,
|
.open = snd_usbmidi_output_open,
|
||||||
.close = snd_usbmidi_output_close,
|
.close = snd_usbmidi_output_close,
|
||||||
.trigger = snd_usbmidi_output_trigger,
|
.trigger = snd_usbmidi_output_trigger,
|
||||||
|
.drain = snd_usbmidi_output_drain,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct snd_rawmidi_ops snd_usbmidi_input_ops = {
|
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
|
.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.
|
* Frees an input endpoint.
|
||||||
* May be called when ep hasn't been initialized completely.
|
* May be called when ep hasn't been initialized completely.
|
||||||
*/
|
*/
|
||||||
static void snd_usbmidi_in_endpoint_delete(struct snd_usb_midi_in_endpoint* ep)
|
static void snd_usbmidi_in_endpoint_delete(struct snd_usb_midi_in_endpoint* ep)
|
||||||
{
|
{
|
||||||
if (ep->urb) {
|
unsigned int i;
|
||||||
usb_buffer_free(ep->umidi->chip->dev,
|
|
||||||
ep->urb->transfer_buffer_length,
|
for (i = 0; i < INPUT_URBS; ++i)
|
||||||
ep->urb->transfer_buffer,
|
if (ep->urbs[i])
|
||||||
ep->urb->transfer_dma);
|
free_urb_and_buffer(ep->umidi, ep->urbs[i],
|
||||||
usb_free_urb(ep->urb);
|
ep->urbs[i]->transfer_buffer_length);
|
||||||
}
|
|
||||||
kfree(ep);
|
kfree(ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -943,6 +1019,7 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,
|
||||||
void* buffer;
|
void* buffer;
|
||||||
unsigned int pipe;
|
unsigned int pipe;
|
||||||
int length;
|
int length;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
rep->in = NULL;
|
rep->in = NULL;
|
||||||
ep = kzalloc(sizeof(*ep), GFP_KERNEL);
|
ep = kzalloc(sizeof(*ep), GFP_KERNEL);
|
||||||
|
@ -950,30 +1027,36 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
ep->umidi = umidi;
|
ep->umidi = umidi;
|
||||||
|
|
||||||
ep->urb = usb_alloc_urb(0, GFP_KERNEL);
|
for (i = 0; i < INPUT_URBS; ++i) {
|
||||||
if (!ep->urb) {
|
ep->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
|
||||||
snd_usbmidi_in_endpoint_delete(ep);
|
if (!ep->urbs[i]) {
|
||||||
return -ENOMEM;
|
snd_usbmidi_in_endpoint_delete(ep);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ep_info->in_interval)
|
if (ep_info->in_interval)
|
||||||
pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->in_ep);
|
pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->in_ep);
|
||||||
else
|
else
|
||||||
pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep);
|
pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep);
|
||||||
length = usb_maxpacket(umidi->chip->dev, pipe, 0);
|
length = usb_maxpacket(umidi->chip->dev, pipe, 0);
|
||||||
buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL,
|
for (i = 0; i < INPUT_URBS; ++i) {
|
||||||
&ep->urb->transfer_dma);
|
buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL,
|
||||||
if (!buffer) {
|
&ep->urbs[i]->transfer_dma);
|
||||||
snd_usbmidi_in_endpoint_delete(ep);
|
if (!buffer) {
|
||||||
return -ENOMEM;
|
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;
|
rep->in = ep;
|
||||||
return 0;
|
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)
|
static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint* ep)
|
||||||
{
|
{
|
||||||
if (ep->urb) {
|
unsigned int i;
|
||||||
usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer,
|
|
||||||
ep->urb->transfer_buffer,
|
for (i = 0; i < OUTPUT_URBS; ++i)
|
||||||
ep->urb->transfer_dma);
|
if (ep->urbs[i].urb)
|
||||||
usb_free_urb(ep->urb);
|
free_urb_and_buffer(ep->umidi, ep->urbs[i].urb,
|
||||||
}
|
ep->max_transfer);
|
||||||
kfree(ep);
|
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_endpoint* rep)
|
||||||
{
|
{
|
||||||
struct snd_usb_midi_out_endpoint* ep;
|
struct snd_usb_midi_out_endpoint* ep;
|
||||||
int i;
|
unsigned int i;
|
||||||
unsigned int pipe;
|
unsigned int pipe;
|
||||||
void* buffer;
|
void* buffer;
|
||||||
|
|
||||||
|
@ -1021,38 +1104,46 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
ep->umidi = umidi;
|
ep->umidi = umidi;
|
||||||
|
|
||||||
ep->urb = usb_alloc_urb(0, GFP_KERNEL);
|
for (i = 0; i < OUTPUT_URBS; ++i) {
|
||||||
if (!ep->urb) {
|
ep->urbs[i].urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||||
snd_usbmidi_out_endpoint_delete(ep);
|
if (!ep->urbs[i].urb) {
|
||||||
return -ENOMEM;
|
snd_usbmidi_out_endpoint_delete(ep);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
ep->urbs[i].ep = ep;
|
||||||
}
|
}
|
||||||
if (ep_info->out_interval)
|
if (ep_info->out_interval)
|
||||||
pipe = usb_sndintpipe(umidi->chip->dev, ep_info->out_ep);
|
pipe = usb_sndintpipe(umidi->chip->dev, ep_info->out_ep);
|
||||||
else
|
else
|
||||||
pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep);
|
pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep);
|
||||||
if (umidi->chip->usb_id == USB_ID(0x0a92, 0x1020)) /* ESI M4U */
|
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;
|
ep->max_transfer = 4;
|
||||||
else
|
else
|
||||||
ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1);
|
ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1);
|
||||||
buffer = usb_buffer_alloc(umidi->chip->dev, ep->max_transfer,
|
for (i = 0; i < OUTPUT_URBS; ++i) {
|
||||||
GFP_KERNEL, &ep->urb->transfer_dma);
|
buffer = usb_buffer_alloc(umidi->chip->dev,
|
||||||
if (!buffer) {
|
ep->max_transfer, GFP_KERNEL,
|
||||||
snd_usbmidi_out_endpoint_delete(ep);
|
&ep->urbs[i].urb->transfer_dma);
|
||||||
return -ENOMEM;
|
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);
|
spin_lock_init(&ep->buffer_lock);
|
||||||
tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep);
|
tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep);
|
||||||
|
init_waitqueue_head(&ep->drain_wait);
|
||||||
|
|
||||||
for (i = 0; i < 0x10; ++i)
|
for (i = 0; i < 0x10; ++i)
|
||||||
if (ep_info->out_cables & (1 << 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)
|
void snd_usbmidi_disconnect(struct list_head* p)
|
||||||
{
|
{
|
||||||
struct snd_usb_midi* umidi;
|
struct snd_usb_midi* umidi;
|
||||||
int i;
|
unsigned int i, j;
|
||||||
|
|
||||||
umidi = list_entry(p, struct snd_usb_midi, list);
|
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];
|
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
|
||||||
if (ep->out)
|
if (ep->out)
|
||||||
tasklet_kill(&ep->out->tasklet);
|
tasklet_kill(&ep->out->tasklet);
|
||||||
if (ep->out && ep->out->urb) {
|
if (ep->out) {
|
||||||
usb_kill_urb(ep->out->urb);
|
for (j = 0; j < OUTPUT_URBS; ++j)
|
||||||
|
usb_kill_urb(ep->out->urbs[j].urb);
|
||||||
if (umidi->usb_protocol_ops->finish_out_endpoint)
|
if (umidi->usb_protocol_ops->finish_out_endpoint)
|
||||||
umidi->usb_protocol_ops->finish_out_endpoint(ep->out);
|
umidi->usb_protocol_ops->finish_out_endpoint(ep->out);
|
||||||
}
|
}
|
||||||
if (ep->in)
|
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 */
|
/* free endpoints here; later call can result in Oops */
|
||||||
if (ep->out) {
|
if (ep->out) {
|
||||||
snd_usbmidi_out_endpoint_delete(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)
|
void snd_usbmidi_input_stop(struct list_head* p)
|
||||||
{
|
{
|
||||||
struct snd_usb_midi* umidi;
|
struct snd_usb_midi* umidi;
|
||||||
int i;
|
unsigned int i, j;
|
||||||
|
|
||||||
umidi = list_entry(p, struct snd_usb_midi, list);
|
umidi = list_entry(p, struct snd_usb_midi, list);
|
||||||
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
|
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
|
||||||
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
|
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
|
||||||
if (ep->in)
|
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)
|
static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)
|
||||||
{
|
{
|
||||||
if (ep) {
|
unsigned int i;
|
||||||
struct urb* urb = ep->urb;
|
|
||||||
|
if (!ep)
|
||||||
|
return;
|
||||||
|
for (i = 0; i < INPUT_URBS; ++i) {
|
||||||
|
struct urb* urb = ep->urbs[i];
|
||||||
urb->dev = ep->umidi->chip->dev;
|
urb->dev = ep->umidi->chip->dev;
|
||||||
snd_usbmidi_submit_urb(urb, GFP_KERNEL);
|
snd_usbmidi_submit_urb(urb, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,7 @@ struct usb_mixer_interface {
|
||||||
u8 rc_buffer[6];
|
u8 rc_buffer[6];
|
||||||
|
|
||||||
u8 audigy2nx_leds[3];
|
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 snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
|
||||||
int ignore_error)
|
int ignore_error)
|
||||||
{
|
{
|
||||||
|
@ -2084,6 +2137,13 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
|
||||||
snd_audigy2nx_proc_read);
|
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);
|
err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto _error;
|
goto _error;
|
||||||
|
|
Loading…
Reference in a new issue