ALSA: usb-audio: Avoid autopm calls after disconnection; Fix missing autopm for MIDI input (Closes: #664068)
svn path=/dists/trunk/linux/; revision=19716
This commit is contained in:
parent
c6aad6ec7c
commit
e5d8d9ad1e
|
@ -6,6 +6,8 @@ linux (3.7.1-1~experimental.3) UNRELEASED; urgency=low
|
||||||
* ath6kl: Do not use virt_addr_valid() (fixes FTBFS on mips,mipsel)
|
* ath6kl: Do not use virt_addr_valid() (fixes FTBFS on mips,mipsel)
|
||||||
* [armel/iop32x,armel/ixp4xx] Make IPV6 a module again due to kernel
|
* [armel/iop32x,armel/ixp4xx] Make IPV6 a module again due to kernel
|
||||||
size limit (fixes FTBFS)
|
size limit (fixes FTBFS)
|
||||||
|
* ALSA: usb-audio: Avoid autopm calls after disconnection; Fix missing
|
||||||
|
autopm for MIDI input (Closes: #664068)
|
||||||
|
|
||||||
-- Ben Hutchings <ben@decadent.org.uk> Sun, 06 Jan 2013 04:39:12 +0000
|
-- Ben Hutchings <ben@decadent.org.uk> Sun, 06 Jan 2013 04:39:12 +0000
|
||||||
|
|
||||||
|
|
101
debian/patches/bugfix/all/ALSA-usb-audio-Avoid-autopm-calls-after-disconnectio.patch
vendored
Normal file
101
debian/patches/bugfix/all/ALSA-usb-audio-Avoid-autopm-calls-after-disconnectio.patch
vendored
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
From: Takashi Iwai <tiwai@suse.de>
|
||||||
|
Date: Mon, 3 Dec 2012 11:12:46 +0100
|
||||||
|
Subject: [1/2] ALSA: usb-audio: Avoid autopm calls after disconnection
|
||||||
|
|
||||||
|
commit 59866da9e4ae54819e3c4e0a8f426bdb0c2ef993 upstream.
|
||||||
|
|
||||||
|
Add a similar protection against the disconnection race and the
|
||||||
|
invalid use of usb instance after disconnection, as well as we've done
|
||||||
|
for the USB audio PCM.
|
||||||
|
|
||||||
|
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=51201
|
||||||
|
|
||||||
|
Reviewd-by: Clemens Ladisch <clemens@ladisch.de>
|
||||||
|
Tested-by: Clemens Ladisch <clemens@ladisch.de>
|
||||||
|
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
||||||
|
---
|
||||||
|
sound/usb/midi.c | 23 ++++++++++++++++++++++-
|
||||||
|
1 file changed, 22 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
|
||||||
|
index eeefbce..c0054ee 100644
|
||||||
|
--- a/sound/usb/midi.c
|
||||||
|
+++ b/sound/usb/midi.c
|
||||||
|
@@ -116,6 +116,7 @@ struct snd_usb_midi {
|
||||||
|
struct list_head list;
|
||||||
|
struct timer_list error_timer;
|
||||||
|
spinlock_t disc_lock;
|
||||||
|
+ struct rw_semaphore disc_rwsem;
|
||||||
|
struct mutex mutex;
|
||||||
|
u32 usb_id;
|
||||||
|
int next_midi_device;
|
||||||
|
@@ -1038,6 +1039,12 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open)
|
||||||
|
struct snd_usb_midi* umidi = substream->rmidi->private_data;
|
||||||
|
struct snd_kcontrol *ctl;
|
||||||
|
|
||||||
|
+ down_read(&umidi->disc_rwsem);
|
||||||
|
+ if (umidi->disconnected) {
|
||||||
|
+ up_read(&umidi->disc_rwsem);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
mutex_lock(&umidi->mutex);
|
||||||
|
if (open) {
|
||||||
|
if (umidi->opened++ == 0 && umidi->roland_load_ctl) {
|
||||||
|
@@ -1056,6 +1063,7 @@ static void substream_open(struct snd_rawmidi_substream *substream, int open)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&umidi->mutex);
|
||||||
|
+ up_read(&umidi->disc_rwsem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
|
||||||
|
@@ -1076,8 +1084,15 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
|
||||||
|
snd_BUG();
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ down_read(&umidi->disc_rwsem);
|
||||||
|
+ if (umidi->disconnected) {
|
||||||
|
+ up_read(&umidi->disc_rwsem);
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
err = usb_autopm_get_interface(umidi->iface);
|
||||||
|
port->autopm_reference = err >= 0;
|
||||||
|
+ up_read(&umidi->disc_rwsem);
|
||||||
|
if (err < 0 && err != -EACCES)
|
||||||
|
return -EIO;
|
||||||
|
substream->runtime->private_data = port;
|
||||||
|
@@ -1092,8 +1107,10 @@ static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
|
||||||
|
struct usbmidi_out_port *port = substream->runtime->private_data;
|
||||||
|
|
||||||
|
substream_open(substream, 0);
|
||||||
|
- if (port->autopm_reference)
|
||||||
|
+ down_read(&umidi->disc_rwsem);
|
||||||
|
+ if (!umidi->disconnected && port->autopm_reference)
|
||||||
|
usb_autopm_put_interface(umidi->iface);
|
||||||
|
+ up_read(&umidi->disc_rwsem);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1403,9 +1420,12 @@ void snd_usbmidi_disconnect(struct list_head* p)
|
||||||
|
* a timer may submit an URB. To reliably break the cycle
|
||||||
|
* a flag under lock must be used
|
||||||
|
*/
|
||||||
|
+ down_write(&umidi->disc_rwsem);
|
||||||
|
spin_lock_irq(&umidi->disc_lock);
|
||||||
|
umidi->disconnected = 1;
|
||||||
|
spin_unlock_irq(&umidi->disc_lock);
|
||||||
|
+ up_write(&umidi->disc_rwsem);
|
||||||
|
+
|
||||||
|
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
|
||||||
|
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
|
||||||
|
if (ep->out)
|
||||||
|
@@ -2117,6 +2137,7 @@ int snd_usbmidi_create(struct snd_card *card,
|
||||||
|
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
|
||||||
|
init_timer(&umidi->error_timer);
|
||||||
|
spin_lock_init(&umidi->disc_lock);
|
||||||
|
+ init_rwsem(&umidi->disc_rwsem);
|
||||||
|
mutex_init(&umidi->mutex);
|
||||||
|
umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
|
||||||
|
le16_to_cpu(umidi->dev->descriptor.idProduct));
|
217
debian/patches/bugfix/all/ALSA-usb-audio-Fix-missing-autopm-for-MIDI-input.patch
vendored
Normal file
217
debian/patches/bugfix/all/ALSA-usb-audio-Fix-missing-autopm-for-MIDI-input.patch
vendored
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
From: Takashi Iwai <tiwai@suse.de>
|
||||||
|
Date: Mon, 3 Dec 2012 11:30:50 +0100
|
||||||
|
Subject: [2/2] ALSA: usb-audio: Fix missing autopm for MIDI input
|
||||||
|
|
||||||
|
commit f5f165418cabf2218eb466c0e94693b8b1aee88b upstream.
|
||||||
|
|
||||||
|
The commit [88a8516a: ALSA: usbaudio: implement USB autosuspend] added
|
||||||
|
the support of autopm for USB MIDI output, but it didn't take the MIDI
|
||||||
|
input into account.
|
||||||
|
|
||||||
|
This patch adds the following for fixing the autopm:
|
||||||
|
- Manage the URB start at the first MIDI input stream open, instead of
|
||||||
|
the time of instance creation
|
||||||
|
- Move autopm code to the common substream_open()
|
||||||
|
- Make snd_usbmidi_input_start/_stop() more robust and add the running
|
||||||
|
state check
|
||||||
|
|
||||||
|
Reviewd-by: Clemens Ladisch <clemens@ladisch.de>
|
||||||
|
Tested-by: Clemens Ladisch <clemens@ladisch.de>
|
||||||
|
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
||||||
|
---
|
||||||
|
sound/usb/midi.c | 88 ++++++++++++++++++++++++++++--------------------------
|
||||||
|
1 file changed, 46 insertions(+), 42 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
|
||||||
|
index c0054ee..34b9bb7 100644
|
||||||
|
--- a/sound/usb/midi.c
|
||||||
|
+++ b/sound/usb/midi.c
|
||||||
|
@@ -126,8 +126,10 @@ struct snd_usb_midi {
|
||||||
|
struct snd_usb_midi_in_endpoint *in;
|
||||||
|
} endpoints[MIDI_MAX_ENDPOINTS];
|
||||||
|
unsigned long input_triggered;
|
||||||
|
- unsigned int opened;
|
||||||
|
+ bool autopm_reference;
|
||||||
|
+ unsigned int opened[2];
|
||||||
|
unsigned char disconnected;
|
||||||
|
+ unsigned char input_running;
|
||||||
|
|
||||||
|
struct snd_kcontrol *roland_load_ctl;
|
||||||
|
};
|
||||||
|
@@ -149,7 +151,6 @@ struct snd_usb_midi_out_endpoint {
|
||||||
|
struct snd_usb_midi_out_endpoint* ep;
|
||||||
|
struct snd_rawmidi_substream *substream;
|
||||||
|
int active;
|
||||||
|
- bool autopm_reference;
|
||||||
|
uint8_t cable; /* cable number << 4 */
|
||||||
|
uint8_t state;
|
||||||
|
#define STATE_UNKNOWN 0
|
||||||
|
@@ -1034,36 +1035,58 @@ static void update_roland_altsetting(struct snd_usb_midi* umidi)
|
||||||
|
snd_usbmidi_input_start(&umidi->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static void substream_open(struct snd_rawmidi_substream *substream, int open)
|
||||||
|
+static int substream_open(struct snd_rawmidi_substream *substream, int dir,
|
||||||
|
+ int open)
|
||||||
|
{
|
||||||
|
struct snd_usb_midi* umidi = substream->rmidi->private_data;
|
||||||
|
struct snd_kcontrol *ctl;
|
||||||
|
+ int err;
|
||||||
|
|
||||||
|
down_read(&umidi->disc_rwsem);
|
||||||
|
if (umidi->disconnected) {
|
||||||
|
up_read(&umidi->disc_rwsem);
|
||||||
|
- return;
|
||||||
|
+ return open ? -ENODEV : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
+ if (!umidi->opened[0] && !umidi->opened[1]) {
|
||||||
|
+ err = usb_autopm_get_interface(umidi->iface);
|
||||||
|
+ umidi->autopm_reference = err >= 0;
|
||||||
|
+ if (err < 0 && err != -EACCES) {
|
||||||
|
+ mutex_unlock(&umidi->mutex);
|
||||||
|
+ up_read(&umidi->disc_rwsem);
|
||||||
|
+ return -EIO;
|
||||||
|
+ }
|
||||||
|
+ if (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);
|
||||||
|
+ update_roland_altsetting(umidi);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
+ umidi->opened[dir]++;
|
||||||
|
+ if (umidi->opened[1])
|
||||||
|
+ snd_usbmidi_input_start(&umidi->list);
|
||||||
|
} 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,
|
||||||
|
+ umidi->opened[dir]--;
|
||||||
|
+ if (!umidi->opened[1])
|
||||||
|
+ snd_usbmidi_input_stop(&umidi->list);
|
||||||
|
+ if (!umidi->opened[0] && !umidi->opened[1]) {
|
||||||
|
+ if (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);
|
||||||
|
+ }
|
||||||
|
+ if (umidi->autopm_reference)
|
||||||
|
+ usb_autopm_put_interface(umidi->iface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&umidi->mutex);
|
||||||
|
up_read(&umidi->disc_rwsem);
|
||||||
|
+ return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
|
||||||
|
@@ -1071,7 +1094,6 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
|
||||||
|
struct snd_usb_midi* umidi = substream->rmidi->private_data;
|
||||||
|
struct usbmidi_out_port* port = NULL;
|
||||||
|
int i, j;
|
||||||
|
- int err;
|
||||||
|
|
||||||
|
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
|
||||||
|
if (umidi->endpoints[i].out)
|
||||||
|
@@ -1085,33 +1107,14 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- down_read(&umidi->disc_rwsem);
|
||||||
|
- if (umidi->disconnected) {
|
||||||
|
- up_read(&umidi->disc_rwsem);
|
||||||
|
- return -ENODEV;
|
||||||
|
- }
|
||||||
|
- err = usb_autopm_get_interface(umidi->iface);
|
||||||
|
- port->autopm_reference = err >= 0;
|
||||||
|
- up_read(&umidi->disc_rwsem);
|
||||||
|
- if (err < 0 && err != -EACCES)
|
||||||
|
- return -EIO;
|
||||||
|
substream->runtime->private_data = port;
|
||||||
|
port->state = STATE_UNKNOWN;
|
||||||
|
- substream_open(substream, 1);
|
||||||
|
- return 0;
|
||||||
|
+ return substream_open(substream, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
|
||||||
|
{
|
||||||
|
- struct snd_usb_midi* umidi = substream->rmidi->private_data;
|
||||||
|
- struct usbmidi_out_port *port = substream->runtime->private_data;
|
||||||
|
-
|
||||||
|
- substream_open(substream, 0);
|
||||||
|
- down_read(&umidi->disc_rwsem);
|
||||||
|
- if (!umidi->disconnected && port->autopm_reference)
|
||||||
|
- usb_autopm_put_interface(umidi->iface);
|
||||||
|
- up_read(&umidi->disc_rwsem);
|
||||||
|
- return 0;
|
||||||
|
+ return substream_open(substream, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
|
||||||
|
@@ -1164,14 +1167,12 @@ 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;
|
||||||
|
+ return substream_open(substream, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream)
|
||||||
|
{
|
||||||
|
- substream_open(substream, 0);
|
||||||
|
- return 0;
|
||||||
|
+ return substream_open(substream, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
|
||||||
|
@@ -2080,12 +2081,15 @@ void snd_usbmidi_input_stop(struct list_head* p)
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
umidi = list_entry(p, struct snd_usb_midi, list);
|
||||||
|
+ if (!umidi->input_running)
|
||||||
|
+ return;
|
||||||
|
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
|
||||||
|
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
|
||||||
|
if (ep->in)
|
||||||
|
for (j = 0; j < INPUT_URBS; ++j)
|
||||||
|
usb_kill_urb(ep->in->urbs[j]);
|
||||||
|
}
|
||||||
|
+ umidi->input_running = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)
|
||||||
|
@@ -2110,8 +2114,11 @@ void snd_usbmidi_input_start(struct list_head* p)
|
||||||
|
int i;
|
||||||
|
|
||||||
|
umidi = list_entry(p, struct snd_usb_midi, list);
|
||||||
|
+ if (umidi->input_running || !umidi->opened[1])
|
||||||
|
+ return;
|
||||||
|
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
|
||||||
|
snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
|
||||||
|
+ umidi->input_running = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -2250,9 +2257,6 @@ int snd_usbmidi_create(struct snd_card *card,
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -70,3 +70,5 @@ debian/ast-disable-autoload.patch
|
||||||
debian/mgag200-disable-autoload.patch
|
debian/mgag200-disable-autoload.patch
|
||||||
clean-modules-without-link-vmlinux.sh.patch
|
clean-modules-without-link-vmlinux.sh.patch
|
||||||
bugfix/all/ath6kl-do-not-use-virt_addr_valid.patch
|
bugfix/all/ath6kl-do-not-use-virt_addr_valid.patch
|
||||||
|
bugfix/all/ALSA-usb-audio-Avoid-autopm-calls-after-disconnectio.patch
|
||||||
|
bugfix/all/ALSA-usb-audio-Fix-missing-autopm-for-MIDI-input.patch
|
||||||
|
|
Loading…
Reference in New Issue