Commit f5f16541 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: usb-audio: Fix missing autopm for MIDI input

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: default avatarClemens Ladisch <clemens@ladisch.de>
Tested-by: default avatarClemens Ladisch <clemens@ladisch.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 59866da9
...@@ -126,8 +126,10 @@ struct snd_usb_midi { ...@@ -126,8 +126,10 @@ struct snd_usb_midi {
struct snd_usb_midi_in_endpoint *in; struct snd_usb_midi_in_endpoint *in;
} endpoints[MIDI_MAX_ENDPOINTS]; } endpoints[MIDI_MAX_ENDPOINTS];
unsigned long input_triggered; unsigned long input_triggered;
unsigned int opened; bool autopm_reference;
unsigned int opened[2];
unsigned char disconnected; unsigned char disconnected;
unsigned char input_running;
struct snd_kcontrol *roland_load_ctl; struct snd_kcontrol *roland_load_ctl;
}; };
...@@ -149,7 +151,6 @@ struct snd_usb_midi_out_endpoint { ...@@ -149,7 +151,6 @@ struct snd_usb_midi_out_endpoint {
struct snd_usb_midi_out_endpoint* ep; struct snd_usb_midi_out_endpoint* ep;
struct snd_rawmidi_substream *substream; struct snd_rawmidi_substream *substream;
int active; int active;
bool autopm_reference;
uint8_t cable; /* cable number << 4 */ uint8_t cable; /* cable number << 4 */
uint8_t state; uint8_t state;
#define STATE_UNKNOWN 0 #define STATE_UNKNOWN 0
...@@ -1034,36 +1035,58 @@ static void update_roland_altsetting(struct snd_usb_midi* umidi) ...@@ -1034,36 +1035,58 @@ static void update_roland_altsetting(struct snd_usb_midi* umidi)
snd_usbmidi_input_start(&umidi->list); 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_usb_midi* umidi = substream->rmidi->private_data;
struct snd_kcontrol *ctl; struct snd_kcontrol *ctl;
int err;
down_read(&umidi->disc_rwsem); down_read(&umidi->disc_rwsem);
if (umidi->disconnected) { if (umidi->disconnected) {
up_read(&umidi->disc_rwsem); up_read(&umidi->disc_rwsem);
return; return open ? -ENODEV : 0;
} }
mutex_lock(&umidi->mutex); mutex_lock(&umidi->mutex);
if (open) { if (open) {
if (umidi->opened++ == 0 && umidi->roland_load_ctl) { if (!umidi->opened[0] && !umidi->opened[1]) {
ctl = umidi->roland_load_ctl; err = usb_autopm_get_interface(umidi->iface);
ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; umidi->autopm_reference = err >= 0;
snd_ctl_notify(umidi->card, 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); 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 { } else {
if (--umidi->opened == 0 && umidi->roland_load_ctl) { umidi->opened[dir]--;
ctl = umidi->roland_load_ctl; if (!umidi->opened[1])
ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_usbmidi_input_stop(&umidi->list);
snd_ctl_notify(umidi->card, 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); SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
}
if (umidi->autopm_reference)
usb_autopm_put_interface(umidi->iface);
} }
} }
mutex_unlock(&umidi->mutex); mutex_unlock(&umidi->mutex);
up_read(&umidi->disc_rwsem); up_read(&umidi->disc_rwsem);
return 0;
} }
static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) 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) ...@@ -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 snd_usb_midi* umidi = substream->rmidi->private_data;
struct usbmidi_out_port* port = NULL; struct usbmidi_out_port* port = NULL;
int i, j; int i, j;
int err;
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
if (umidi->endpoints[i].out) if (umidi->endpoints[i].out)
...@@ -1085,33 +1107,14 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) ...@@ -1085,33 +1107,14 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
return -ENXIO; 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; substream->runtime->private_data = port;
port->state = STATE_UNKNOWN; port->state = STATE_UNKNOWN;
substream_open(substream, 1); return substream_open(substream, 0, 1);
return 0;
} }
static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
{ {
struct snd_usb_midi* umidi = substream->rmidi->private_data; return substream_open(substream, 0, 0);
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;
} }
static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, int up) 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) ...@@ -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) static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)
{ {
substream_open(substream, 1); return substream_open(substream, 1, 1);
return 0;
} }
static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream) static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream)
{ {
substream_open(substream, 0); return substream_open(substream, 1, 0);
return 0;
} }
static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) 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) ...@@ -2080,12 +2081,15 @@ void snd_usbmidi_input_stop(struct list_head* p)
unsigned int i, j; unsigned int i, j;
umidi = list_entry(p, struct snd_usb_midi, list); umidi = list_entry(p, struct snd_usb_midi, list);
if (!umidi->input_running)
return;
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)
for (j = 0; j < INPUT_URBS; ++j) for (j = 0; j < INPUT_URBS; ++j)
usb_kill_urb(ep->in->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) 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) ...@@ -2110,8 +2114,11 @@ void snd_usbmidi_input_start(struct list_head* p)
int i; int i;
umidi = list_entry(p, struct snd_usb_midi, list); 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) for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
snd_usbmidi_input_start_ep(umidi->endpoints[i].in); 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, ...@@ -2250,9 +2257,6 @@ int snd_usbmidi_create(struct snd_card *card,
} }
list_add_tail(&umidi->list, 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; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment