summaryrefslogtreecommitdiffstats
path: root/sound/usb/usbmidi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/usbmidi.c')
-rw-r--r--sound/usb/usbmidi.c208
1 files changed, 155 insertions, 53 deletions
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 0eff19c..6e89b83 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -1,7 +1,7 @@
/*
* usbmidi.c - ALSA USB MIDI driver
*
- * Copyright (c) 2002-2007 Clemens Ladisch
+ * Copyright (c) 2002-2009 Clemens Ladisch
* All rights reserved.
*
* Based on the OSS usb-midi driver by NAGANO Daisuke,
@@ -47,6 +47,7 @@
#include <linux/usb.h>
#include <linux/wait.h>
#include <sound/core.h>
+#include <sound/control.h>
#include <sound/rawmidi.h>
#include <sound/asequencer.h>
#include "usbaudio.h"
@@ -101,7 +102,8 @@ struct usb_protocol_ops {
};
struct snd_usb_midi {
- struct snd_usb_audio *chip;
+ struct usb_device *dev;
+ struct snd_card *card;
struct usb_interface *iface;
const struct snd_usb_audio_quirk *quirk;
struct snd_rawmidi *rmidi;
@@ -109,13 +111,19 @@ struct snd_usb_midi {
struct list_head list;
struct timer_list error_timer;
spinlock_t disc_lock;
+ struct mutex mutex;
+ u32 usb_id;
+ int next_midi_device;
struct snd_usb_midi_endpoint {
struct snd_usb_midi_out_endpoint *out;
struct snd_usb_midi_in_endpoint *in;
} endpoints[MIDI_MAX_ENDPOINTS];
unsigned long input_triggered;
+ unsigned int opened;
unsigned char disconnected;
+
+ struct snd_kcontrol *roland_load_ctl;
};
struct snd_usb_midi_out_endpoint {
@@ -255,7 +263,7 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb)
}
}
- urb->dev = ep->umidi->chip->dev;
+ urb->dev = ep->umidi->dev;
snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
}
@@ -296,7 +304,7 @@ static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep)
unsigned long flags;
spin_lock_irqsave(&ep->buffer_lock, flags);
- if (ep->umidi->chip->shutdown) {
+ if (ep->umidi->disconnected) {
spin_unlock_irqrestore(&ep->buffer_lock, flags);
return;
}
@@ -312,7 +320,7 @@ static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep)
dump_urb("sending", urb->transfer_buffer,
urb->transfer_buffer_length);
- urb->dev = ep->umidi->chip->dev;
+ urb->dev = ep->umidi->dev;
if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0)
break;
ep->active_urbs |= 1 << urb_index;
@@ -349,7 +357,7 @@ static void snd_usbmidi_error_timer(unsigned long data)
if (in && in->error_resubmit) {
in->error_resubmit = 0;
for (j = 0; j < INPUT_URBS; ++j) {
- in->urbs[j]->dev = umidi->chip->dev;
+ in->urbs[j]->dev = umidi->dev;
snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC);
}
}
@@ -369,7 +377,7 @@ static int send_bulk_static_data(struct snd_usb_midi_out_endpoint* ep,
return -ENOMEM;
dump_urb("sending", buf, len);
if (ep->urbs[0].urb)
- err = usb_bulk_msg(ep->umidi->chip->dev, ep->urbs[0].urb->pipe,
+ err = usb_bulk_msg(ep->umidi->dev, ep->urbs[0].urb->pipe,
buf, len, NULL, 250);
kfree(buf);
return err;
@@ -724,8 +732,7 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
if (!ep->ports[0].active)
return;
- count = snd_usb_get_speed(ep->umidi->chip->dev) == USB_SPEED_HIGH
- ? 1 : 2;
+ count = snd_usb_get_speed(ep->umidi->dev) == USB_SPEED_HIGH ? 1 : 2;
count = snd_rawmidi_transmit(ep->ports[0].substream,
urb->transfer_buffer,
count);
@@ -879,6 +886,50 @@ static struct usb_protocol_ops snd_usbmidi_emagic_ops = {
};
+static void update_roland_altsetting(struct snd_usb_midi* umidi)
+{
+ struct usb_interface *intf;
+ struct usb_host_interface *hostif;
+ struct usb_interface_descriptor *intfd;
+ int is_light_load;
+
+ intf = umidi->iface;
+ is_light_load = intf->cur_altsetting != intf->altsetting;
+ if (umidi->roland_load_ctl->private_value == is_light_load)
+ return;
+ hostif = &intf->altsetting[umidi->roland_load_ctl->private_value];
+ intfd = get_iface_desc(hostif);
+ snd_usbmidi_input_stop(&umidi->list);
+ usb_set_interface(umidi->dev, intfd->bInterfaceNumber,
+ intfd->bAlternateSetting);
+ snd_usbmidi_input_start(&umidi->list);
+}
+
+static void substream_open(struct snd_rawmidi_substream *substream, int open)
+{
+ struct snd_usb_midi* umidi = substream->rmidi->private_data;
+ struct snd_kcontrol *ctl;
+
+ mutex_lock(&umidi->mutex);
+ if (open) {
+ if (umidi->opened++ == 0 && umidi->roland_load_ctl) {
+ ctl = umidi->roland_load_ctl;
+ ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(umidi->card,
+ SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
+ update_roland_altsetting(umidi);
+ }
+ } else {
+ if (--umidi->opened == 0 && umidi->roland_load_ctl) {
+ ctl = umidi->roland_load_ctl;
+ ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(umidi->card,
+ SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
+ }
+ }
+ mutex_unlock(&umidi->mutex);
+}
+
static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
{
struct snd_usb_midi* umidi = substream->rmidi->private_data;
@@ -898,11 +949,13 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
}
substream->runtime->private_data = port;
port->state = STATE_UNKNOWN;
+ substream_open(substream, 1);
return 0;
}
static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
{
+ substream_open(substream, 0);
return 0;
}
@@ -912,7 +965,7 @@ static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream,
port->active = up;
if (up) {
- if (port->ep->umidi->chip->shutdown) {
+ if (port->ep->umidi->disconnected) {
/* gobble up remaining bytes to prevent wait in
* snd_rawmidi_drain_output */
while (!snd_rawmidi_transmit_empty(substream))
@@ -954,11 +1007,13 @@ static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream)
static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)
{
+ substream_open(substream, 1);
return 0;
}
static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream)
{
+ substream_open(substream, 0);
return 0;
}
@@ -988,7 +1043,7 @@ static struct snd_rawmidi_ops snd_usbmidi_input_ops = {
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,
+ usb_buffer_free(umidi->dev, buffer_length,
urb->transfer_buffer, urb->transfer_dma);
usb_free_urb(urb);
}
@@ -1035,24 +1090,24 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,
}
}
if (ep_info->in_interval)
- pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->in_ep);
+ pipe = usb_rcvintpipe(umidi->dev, ep_info->in_ep);
else
- pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep);
- length = usb_maxpacket(umidi->chip->dev, pipe, 0);
+ pipe = usb_rcvbulkpipe(umidi->dev, ep_info->in_ep);
+ length = usb_maxpacket(umidi->dev, pipe, 0);
for (i = 0; i < INPUT_URBS; ++i) {
- buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL,
+ buffer = usb_buffer_alloc(umidi->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,
+ usb_fill_int_urb(ep->urbs[i], umidi->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,
+ usb_fill_bulk_urb(ep->urbs[i], umidi->dev,
pipe, buffer, length,
snd_usbmidi_in_urb_complete, ep);
ep->urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
@@ -1062,15 +1117,6 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,
return 0;
}
-static unsigned int snd_usbmidi_count_bits(unsigned int x)
-{
- unsigned int bits;
-
- for (bits = 0; x; ++bits)
- x &= x - 1;
- return bits;
-}
-
/*
* Frees an output endpoint.
* May be called when ep hasn't been initialized completely.
@@ -1113,15 +1159,15 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
ep->urbs[i].ep = ep;
}
if (ep_info->out_interval)
- pipe = usb_sndintpipe(umidi->chip->dev, ep_info->out_ep);
+ pipe = usb_sndintpipe(umidi->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 */
+ pipe = usb_sndbulkpipe(umidi->dev, ep_info->out_ep);
+ if (umidi->usb_id == USB_ID(0x0a92, 0x1020)) /* ESI M4U */
ep->max_transfer = 4;
else
- ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1);
+ ep->max_transfer = usb_maxpacket(umidi->dev, pipe, 1);
for (i = 0; i < OUTPUT_URBS; ++i) {
- buffer = usb_buffer_alloc(umidi->chip->dev,
+ buffer = usb_buffer_alloc(umidi->dev,
ep->max_transfer, GFP_KERNEL,
&ep->urbs[i].urb->transfer_dma);
if (!buffer) {
@@ -1129,12 +1175,12 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
return -ENOMEM;
}
if (ep_info->out_interval)
- usb_fill_int_urb(ep->urbs[i].urb, umidi->chip->dev,
+ usb_fill_int_urb(ep->urbs[i].urb, umidi->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,
+ usb_fill_bulk_urb(ep->urbs[i].urb, umidi->dev,
pipe, buffer, ep->max_transfer,
snd_usbmidi_out_urb_complete,
&ep->urbs[i]);
@@ -1172,6 +1218,7 @@ static void snd_usbmidi_free(struct snd_usb_midi* umidi)
if (ep->in)
snd_usbmidi_in_endpoint_delete(ep->in);
}
+ mutex_destroy(&umidi->mutex);
kfree(umidi);
}
@@ -1367,7 +1414,7 @@ static struct port_info *find_port_info(struct snd_usb_midi* umidi, int number)
int i;
for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_info); ++i) {
- if (snd_usbmidi_port_info[i].id == umidi->chip->usb_id &&
+ if (snd_usbmidi_port_info[i].id == umidi->usb_id &&
snd_usbmidi_port_info[i].port == number)
return &snd_usbmidi_port_info[i];
}
@@ -1405,7 +1452,7 @@ static void snd_usbmidi_init_substream(struct snd_usb_midi* umidi,
port_info = find_port_info(umidi, number);
name_format = port_info ? port_info->name : "%s MIDI %d";
snprintf(substream->name, sizeof(substream->name),
- name_format, umidi->chip->card->shortname, number + 1);
+ name_format, umidi->card->shortname, number + 1);
*rsubstream = substream;
}
@@ -1503,7 +1550,7 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
endpoints[epidx].out_ep = usb_endpoint_num(ep);
if (usb_endpoint_xfer_int(ep))
endpoints[epidx].out_interval = ep->bInterval;
- else if (snd_usb_get_speed(umidi->chip->dev) == USB_SPEED_LOW)
+ else if (snd_usb_get_speed(umidi->dev) == USB_SPEED_LOW)
/*
* Low speed bulk transfers don't exist, so
* force interrupt transfers for devices like
@@ -1523,7 +1570,7 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
endpoints[epidx].in_ep = usb_endpoint_num(ep);
if (usb_endpoint_xfer_int(ep))
endpoints[epidx].in_interval = ep->bInterval;
- else if (snd_usb_get_speed(umidi->chip->dev) == USB_SPEED_LOW)
+ else if (snd_usb_get_speed(umidi->dev) == USB_SPEED_LOW)
endpoints[epidx].in_interval = 1;
endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n",
@@ -1533,6 +1580,52 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
return 0;
}
+static int roland_load_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[] = { "High Load", "Light Load" };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 2;
+ if (info->value.enumerated.item > 1)
+ info->value.enumerated.item = 1;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int roland_load_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ value->value.enumerated.item[0] = kcontrol->private_value;
+ return 0;
+}
+
+static int roland_load_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_usb_midi* umidi = kcontrol->private_data;
+ int changed;
+
+ if (value->value.enumerated.item[0] > 1)
+ return -EINVAL;
+ mutex_lock(&umidi->mutex);
+ changed = value->value.enumerated.item[0] != kcontrol->private_value;
+ if (changed)
+ kcontrol->private_value = value->value.enumerated.item[0];
+ mutex_unlock(&umidi->mutex);
+ return changed;
+}
+
+static struct snd_kcontrol_new roland_load_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "MIDI Input Mode",
+ .info = roland_load_info,
+ .get = roland_load_get,
+ .put = roland_load_put,
+ .private_value = 1,
+};
+
/*
* On Roland devices, use the second alternate setting to be able to use
* the interrupt input endpoint.
@@ -1556,8 +1649,12 @@ static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi* umidi)
snd_printdd(KERN_INFO "switching to altsetting %d with int ep\n",
intfd->bAlternateSetting);
- usb_set_interface(umidi->chip->dev, intfd->bInterfaceNumber,
+ usb_set_interface(umidi->dev, intfd->bInterfaceNumber,
intfd->bAlternateSetting);
+
+ umidi->roland_load_ctl = snd_ctl_new1(&roland_load_ctl, umidi);
+ if (snd_ctl_add(umidi->card, umidi->roland_load_ctl) < 0)
+ umidi->roland_load_ctl = NULL;
}
/*
@@ -1573,7 +1670,7 @@ static int snd_usbmidi_detect_endpoints(struct snd_usb_midi* umidi,
struct usb_endpoint_descriptor* epd;
int i, out_eps = 0, in_eps = 0;
- if (USB_ID_VENDOR(umidi->chip->usb_id) == 0x0582)
+ if (USB_ID_VENDOR(umidi->usb_id) == 0x0582)
snd_usbmidi_switch_roland_altsetting(umidi);
if (endpoint[0].out_ep || endpoint[0].in_ep)
@@ -1760,12 +1857,12 @@ static int snd_usbmidi_create_rawmidi(struct snd_usb_midi* umidi,
struct snd_rawmidi *rmidi;
int err;
- err = snd_rawmidi_new(umidi->chip->card, "USB MIDI",
- umidi->chip->next_midi_device++,
+ err = snd_rawmidi_new(umidi->card, "USB MIDI",
+ umidi->next_midi_device++,
out_ports, in_ports, &rmidi);
if (err < 0)
return err;
- strcpy(rmidi->name, umidi->chip->card->shortname);
+ strcpy(rmidi->name, umidi->card->shortname);
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -1804,7 +1901,7 @@ static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* 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->dev;
snd_usbmidi_submit_urb(urb, GFP_KERNEL);
}
}
@@ -1825,9 +1922,10 @@ void snd_usbmidi_input_start(struct list_head* p)
/*
* Creates and registers everything needed for a MIDI streaming interface.
*/
-int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
- struct usb_interface* iface,
- const struct snd_usb_audio_quirk* quirk)
+int snd_usbmidi_create(struct snd_card *card,
+ struct usb_interface* iface,
+ struct list_head *midi_list,
+ const struct snd_usb_audio_quirk* quirk)
{
struct snd_usb_midi* umidi;
struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS];
@@ -1837,12 +1935,16 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
umidi = kzalloc(sizeof(*umidi), GFP_KERNEL);
if (!umidi)
return -ENOMEM;
- umidi->chip = chip;
+ umidi->dev = interface_to_usbdev(iface);
+ umidi->card = card;
umidi->iface = iface;
umidi->quirk = quirk;
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
init_timer(&umidi->error_timer);
spin_lock_init(&umidi->disc_lock);
+ mutex_init(&umidi->mutex);
+ umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
+ le16_to_cpu(umidi->dev->descriptor.idProduct));
umidi->error_timer.function = snd_usbmidi_error_timer;
umidi->error_timer.data = (unsigned long)umidi;
@@ -1851,7 +1953,7 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
switch (quirk ? quirk->type : QUIRK_MIDI_STANDARD_INTERFACE) {
case QUIRK_MIDI_STANDARD_INTERFACE:
err = snd_usbmidi_get_ms_info(umidi, endpoints);
- if (chip->usb_id == USB_ID(0x0763, 0x0150)) /* M-Audio Uno */
+ if (umidi->usb_id == USB_ID(0x0763, 0x0150)) /* M-Audio Uno */
umidi->usb_protocol_ops =
&snd_usbmidi_maudio_broken_running_status_ops;
break;
@@ -1887,7 +1989,7 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
* interface 0, so we have to make sure that the USB core looks
* again at interface 0 by calling usb_set_interface() on it.
*/
- usb_set_interface(umidi->chip->dev, 0, 0);
+ usb_set_interface(umidi->dev, 0, 0);
err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
break;
case QUIRK_MIDI_EMAGIC:
@@ -1914,8 +2016,8 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
out_ports = 0;
in_ports = 0;
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
- out_ports += snd_usbmidi_count_bits(endpoints[i].out_cables);
- in_ports += snd_usbmidi_count_bits(endpoints[i].in_cables);
+ out_ports += hweight16(endpoints[i].out_cables);
+ in_ports += hweight16(endpoints[i].in_cables);
}
err = snd_usbmidi_create_rawmidi(umidi, out_ports, in_ports);
if (err < 0) {
@@ -1933,14 +2035,14 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
return err;
}
- list_add(&umidi->list, &umidi->chip->midi_list);
+ list_add_tail(&umidi->list, midi_list);
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
return 0;
}
-EXPORT_SYMBOL(snd_usb_create_midi_interface);
+EXPORT_SYMBOL(snd_usbmidi_create);
EXPORT_SYMBOL(snd_usbmidi_input_stop);
EXPORT_SYMBOL(snd_usbmidi_input_start);
EXPORT_SYMBOL(snd_usbmidi_disconnect);
OpenPOWER on IntegriCloud